github.com/haraldrudell/parl@v0.4.176/mains/process-start-other.go (about) 1 //go:build !darwin 2 3 /* 4 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 5 ISC License 6 */ 7 8 package mains 9 10 import ( 11 "bytes" 12 "fmt" 13 "io/ioutil" 14 "os" 15 "strconv" 16 "strings" 17 "time" 18 19 "github.com/haraldrudell/parl/perrors" 20 ) 21 22 const ( 23 // format absolute path into /proc directory 24 filePrintf = "/proc/%d/stat" 25 rightParenthesisByte = byte(')') 26 // the number of clock ticks per second 27 // - getconf CLK_TCK 28 // - 100 29 // - man sysconf, clock ticks 30 // - The number of clock ticks per second 31 clockTicksPerSecond = 100 32 clockTickNs = time.Second / clockTicksPerSecond 33 // starttime index, 1-based, in /proc/[pid]/stat space-separated file. 34 // - man proc, /proc/[pid]/stat, entry (22) 1-based 35 // - starttime %llu 36 // - The time the process started after system boot 37 // - the value is expressed in clock ticks (divide by sysconf(_SC_CLK_TCK)). 38 startTimeIndex = 21 - 2 39 // path of /proc/uptime 40 // - This file contains two numbers 41 // - man proc 42 // - values in seconds: the uptime of the system 43 // - cat /proc/uptime 44 // - 5422217.66 21636302.50 45 procUptime = "/proc/uptime" 46 procUptimeField = 0 47 ) 48 49 // ProcessStartTime returns start time for process pid with second resolution 50 // - panic on troubles 51 func ProcessStartTime() (createTime time.Time) { 52 var err error 53 if createTime, err = ProcessStart(os.Getpid()); err != nil { 54 panic(err) 55 } 56 return 57 } 58 59 // ProcessStart returns start time for process pid with second resolution 60 func ProcessStart(pid int) (processStart time.Time, err error) { 61 62 // get system uptime resolution 10 ms 63 var uptime time.Duration 64 if uptime, err = systemUptime(); err != nil { 65 return 66 } 67 68 // get process start time in clock ticks 69 var startTicks int64 70 if startTicks, err = processStartTicks(pid); err != nil { 71 return 72 } 73 74 // get process duration ± 20 ms 75 var processDuration = uptime - time.Duration(startTicks)*clockTickNs 76 77 // calculate process start time 78 processStart = time.Now().Add(-processDuration).Truncate(time.Second) 79 80 return 81 } 82 83 // SystemUpSince returns boot time second resolution 84 func SystemUpSince() (upSince time.Time, err error) { 85 var uptime time.Duration 86 if uptime, err = systemUptime(); err != nil { 87 return 88 } 89 upSince = time.Now().Add(-uptime).Truncate(time.Second) 90 return 91 } 92 93 // systemUptime returns host up time resolution 10 ms 94 func systemUptime() (uptime time.Duration, err error) { 95 96 // read /proc/uptime 97 var data []byte 98 if data, err = ioutil.ReadFile(procUptime); perrors.Is(&err, "ioutil.ReadFile %w", err) { 99 return 100 } 101 102 // extract numeric string 103 var fields = bytes.Fields(data) 104 if procUptimeField >= len(fields) { 105 err = perrors.ErrorfPF("content too short: %q", procUptime) 106 return 107 } 108 // remove the period 109 var uptimeS = strings.Replace(string(fields[procUptimeField]), ".", "", 1) 110 111 // convert from unit 10 ms to time.Duration 112 var u64 uint64 113 if u64, err = strconv.ParseUint(uptimeS, 10, 64); perrors.Is(&err, "ParseUint %w", err) { 114 return 115 } 116 uptime = time.Duration(u64) * 10 * time.Millisecond 117 118 return 119 } 120 121 // processStartTicks returns the time in system clock ticks since boot when the process was started 122 func processStartTicks(pid int) (clockTicks int64, err error) { 123 124 // read /proc/n/stat 125 var data []byte 126 var filename = fmt.Sprintf(filePrintf, pid) 127 if data, err = ioutil.ReadFile(filename); perrors.Is(&err, "ioutil.ReadFile %w", err) { 128 return 129 } 130 131 // get tick count 132 var index int 133 if index = bytes.LastIndexByte(data, rightParenthesisByte); index == -1 { 134 err = perrors.ErrorfPF("no %q found in %q", rightParenthesisByte, filename) 135 return 136 } 137 var fields = bytes.Fields(data[index+1:]) 138 if startTimeIndex >= len(fields) { 139 err = perrors.ErrorfPF("content too short: %q", filename) 140 return 141 } 142 clockTicks, err = strconv.ParseInt(string(fields[startTimeIndex]), 10, 64) 143 144 return 145 }