github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/process/process.go (about) 1 package process 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "runtime" 8 "sort" 9 "sync" 10 "syscall" 11 "time" 12 13 "github.com/isyscore/isc-gobase/system/common" 14 "github.com/isyscore/isc-gobase/system/cpu" 15 "github.com/isyscore/isc-gobase/system/mem" 16 "github.com/isyscore/isc-gobase/system/net" 17 ) 18 19 var ( 20 invoke common.Invoker = common.Invoke{} 21 ErrorNoChildren = errors.New("process does not have children") 22 ErrorProcessNotRunning = errors.New("process does not exist") 23 ) 24 25 type Process struct { 26 Pid int32 `json:"pid"` 27 name string 28 status string 29 parent int32 30 parentMutex sync.RWMutex // for windows ppid cache 31 numCtxSwitches *NumCtxSwitchesStat 32 uids []int32 33 gids []int32 34 groups []int32 35 numThreads int32 36 memInfo *MemoryInfoStat 37 sigInfo *SignalInfoStat 38 createTime int64 39 40 lastCPUTimes *cpu.TimesStat 41 lastCPUTime time.Time 42 43 tgid int32 44 } 45 46 type OpenFilesStat struct { 47 Path string `json:"path"` 48 Fd uint64 `json:"fd"` 49 } 50 51 type MemoryInfoStat struct { 52 RSS uint64 `json:"rss"` // bytes 53 VMS uint64 `json:"vms"` // bytes 54 HWM uint64 `json:"hwm"` // bytes 55 Data uint64 `json:"data"` // bytes 56 Stack uint64 `json:"stack"` // bytes 57 Locked uint64 `json:"locked"` // bytes 58 Swap uint64 `json:"swap"` // bytes 59 } 60 61 type SignalInfoStat struct { 62 PendingProcess uint64 `json:"pending_process"` 63 PendingThread uint64 `json:"pending_thread"` 64 Blocked uint64 `json:"blocked"` 65 Ignored uint64 `json:"ignored"` 66 Caught uint64 `json:"caught"` 67 } 68 69 type RlimitStat struct { 70 Resource int32 `json:"resource"` 71 Soft int32 `json:"soft"` //TODO too small. needs to be uint64 72 Hard int32 `json:"hard"` //TODO too small. needs to be uint64 73 Used uint64 `json:"used"` 74 } 75 76 type IOCountersStat struct { 77 ReadCount uint64 `json:"readCount"` 78 WriteCount uint64 `json:"writeCount"` 79 ReadBytes uint64 `json:"readBytes"` 80 WriteBytes uint64 `json:"writeBytes"` 81 } 82 83 type NumCtxSwitchesStat struct { 84 Voluntary int64 `json:"voluntary"` 85 Involuntary int64 `json:"involuntary"` 86 } 87 88 type PageFaultsStat struct { 89 MinorFaults uint64 `json:"minorFaults"` 90 MajorFaults uint64 `json:"majorFaults"` 91 ChildMinorFaults uint64 `json:"childMinorFaults"` 92 ChildMajorFaults uint64 `json:"childMajorFaults"` 93 } 94 95 // Resource limit constants are from /usr/include/x86_64-linux-gnu/bits/resource.h 96 // from libc6-dev package in Ubuntu 16.10 97 const ( 98 RLIMIT_CPU int32 = 0 99 RLIMIT_FSIZE int32 = 1 100 RLIMIT_DATA int32 = 2 101 RLIMIT_STACK int32 = 3 102 RLIMIT_CORE int32 = 4 103 RLIMIT_RSS int32 = 5 104 RLIMIT_NPROC int32 = 6 105 RLIMIT_NOFILE int32 = 7 106 RLIMIT_MEMLOCK int32 = 8 107 RLIMIT_AS int32 = 9 108 RLIMIT_LOCKS int32 = 10 109 RLIMIT_SIGPENDING int32 = 11 110 RLIMIT_MSGQUEUE int32 = 12 111 RLIMIT_NICE int32 = 13 112 RLIMIT_RTPRIO int32 = 14 113 RLIMIT_RTTIME int32 = 15 114 ) 115 116 func (p *Process) String() string { 117 s, _ := json.Marshal(p) 118 return string(s) 119 } 120 121 func (o OpenFilesStat) String() string { 122 s, _ := json.Marshal(o) 123 return string(s) 124 } 125 126 func (m MemoryInfoStat) String() string { 127 s, _ := json.Marshal(m) 128 return string(s) 129 } 130 131 func (r RlimitStat) String() string { 132 s, _ := json.Marshal(r) 133 return string(s) 134 } 135 136 func (i IOCountersStat) String() string { 137 s, _ := json.Marshal(i) 138 return string(s) 139 } 140 141 func (p NumCtxSwitchesStat) String() string { 142 s, _ := json.Marshal(p) 143 return string(s) 144 } 145 146 // Pids returns a slice of process ID list which are running now. 147 func Pids() ([]int32, error) { 148 return PidsWithContext(context.Background()) 149 } 150 151 func PidsWithContext(ctx context.Context) ([]int32, error) { 152 pids, err := pidsWithContext(ctx) 153 sort.Slice(pids, func(i, j int) bool { return pids[i] < pids[j] }) 154 return pids, err 155 } 156 157 // Processes returns a slice of pointers to Process structs for all 158 // currently running processes. 159 func Processes() ([]*Process, error) { 160 return ProcessesWithContext(context.Background()) 161 } 162 163 // NewProcess creates a new Process instance, it only stores the pid and 164 // checks that the process exists. Other method on Process can be used 165 // to get more information about the process. An error will be returned 166 // if the process does not exist. 167 func NewProcess(pid int32) (*Process, error) { 168 return NewProcessWithContext(context.Background(), pid) 169 } 170 171 func NewProcessWithContext(ctx context.Context, pid int32) (*Process, error) { 172 p := &Process{ 173 Pid: pid, 174 } 175 176 exists, err := PidExistsWithContext(ctx, pid) 177 if err != nil { 178 return p, err 179 } 180 if !exists { 181 return p, ErrorProcessNotRunning 182 } 183 _, _ = p.CreateTimeWithContext(ctx) 184 return p, nil 185 } 186 187 func PidExists(pid int32) (bool, error) { 188 return PidExistsWithContext(context.Background(), pid) 189 } 190 191 // Background returns true if the process is in background, false otherwise. 192 func (p *Process) Background() (bool, error) { 193 return p.BackgroundWithContext(context.Background()) 194 } 195 196 func (p *Process) BackgroundWithContext(ctx context.Context) (bool, error) { 197 fg, err := p.ForegroundWithContext(ctx) 198 if err != nil { 199 return false, err 200 } 201 return !fg, err 202 } 203 204 // If interval is 0, return difference from last call(non-blocking). 205 // If interval > 0, wait interval sec and return diffrence between start and end. 206 func (p *Process) Percent(interval time.Duration) (float64, error) { 207 return p.PercentWithContext(context.Background(), interval) 208 } 209 210 func (p *Process) PercentWithContext(ctx context.Context, interval time.Duration) (float64, error) { 211 cpuTimes, err := p.TimesWithContext(ctx) 212 if err != nil { 213 return 0, err 214 } 215 now := time.Now() 216 217 if interval > 0 { 218 p.lastCPUTimes = cpuTimes 219 p.lastCPUTime = now 220 if err := common.Sleep(ctx, interval); err != nil { 221 return 0, err 222 } 223 cpuTimes, err = p.TimesWithContext(ctx) 224 now = time.Now() 225 if err != nil { 226 return 0, err 227 } 228 } else { 229 if p.lastCPUTimes == nil { 230 // invoked first time 231 p.lastCPUTimes = cpuTimes 232 p.lastCPUTime = now 233 return 0, nil 234 } 235 } 236 237 numcpu := runtime.NumCPU() 238 delta := (now.Sub(p.lastCPUTime).Seconds()) * float64(numcpu) 239 ret := calculatePercent(p.lastCPUTimes, cpuTimes, delta, numcpu) 240 p.lastCPUTimes = cpuTimes 241 p.lastCPUTime = now 242 return ret, nil 243 } 244 245 // IsRunning returns whether the process is still running or not. 246 func (p *Process) IsRunning() (bool, error) { 247 return p.IsRunningWithContext(context.Background()) 248 } 249 250 func (p *Process) IsRunningWithContext(ctx context.Context) (bool, error) { 251 createTime, err := p.CreateTimeWithContext(ctx) 252 if err != nil { 253 return false, err 254 } 255 p2, err := NewProcessWithContext(ctx, p.Pid) 256 if err == ErrorProcessNotRunning { 257 return false, nil 258 } 259 createTime2, err := p2.CreateTimeWithContext(ctx) 260 if err != nil { 261 return false, err 262 } 263 return createTime == createTime2, nil 264 } 265 266 // CreateTime returns created time of the process in milliseconds since the epoch, in UTC. 267 func (p *Process) CreateTime() (int64, error) { 268 return p.CreateTimeWithContext(context.Background()) 269 } 270 271 func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) { 272 if p.createTime != 0 { 273 return p.createTime, nil 274 } 275 createTime, err := p.createTimeWithContext(ctx) 276 p.createTime = createTime 277 return p.createTime, err 278 } 279 280 func calculatePercent(t1, t2 *cpu.TimesStat, delta float64, numcpu int) float64 { 281 if delta == 0 { 282 return 0 283 } 284 delta_proc := t2.Total() - t1.Total() 285 overall_percent := ((delta_proc / delta) * 100) * float64(numcpu) 286 return overall_percent 287 } 288 289 // MemoryPercent returns how many percent of the total RAM this process uses 290 func (p *Process) MemoryPercent() (float32, error) { 291 return p.MemoryPercentWithContext(context.Background()) 292 } 293 294 func (p *Process) MemoryPercentWithContext(ctx context.Context) (float32, error) { 295 machineMemory, err := mem.VirtualMemoryWithContext(ctx) 296 if err != nil { 297 return 0, err 298 } 299 total := machineMemory.Total 300 301 processMemory, err := p.MemoryInfoWithContext(ctx) 302 if err != nil { 303 return 0, err 304 } 305 used := processMemory.RSS 306 307 return 100 * float32(used) / float32(total), nil 308 } 309 310 // CPUPercent returns how many percent of the CPU time this process uses 311 func (p *Process) CPUPercent() (float64, error) { 312 return p.CPUPercentWithContext(context.Background()) 313 } 314 315 func (p *Process) CPUPercentWithContext(ctx context.Context) (float64, error) { 316 crt_time, err := p.createTimeWithContext(ctx) 317 if err != nil { 318 return 0, err 319 } 320 321 cput, err := p.TimesWithContext(ctx) 322 if err != nil { 323 return 0, err 324 } 325 326 created := time.Unix(0, crt_time*int64(time.Millisecond)) 327 totalTime := time.Since(created).Seconds() 328 if totalTime <= 0 { 329 return 0, nil 330 } 331 332 return 100 * cput.Total() / totalTime, nil 333 } 334 335 // Groups returns all group IDs(include supplementary groups) of the process as a slice of the int 336 func (p *Process) Groups() ([]int32, error) { 337 return p.GroupsWithContext(context.Background()) 338 } 339 340 // Ppid returns Parent Process ID of the process. 341 func (p *Process) Ppid() (int32, error) { 342 return p.PpidWithContext(context.Background()) 343 } 344 345 // Name returns name of the process. 346 func (p *Process) Name() (string, error) { 347 return p.NameWithContext(context.Background()) 348 } 349 350 // Exe returns executable path of the process. 351 func (p *Process) Exe() (string, error) { 352 return p.ExeWithContext(context.Background()) 353 } 354 355 // Cmdline returns the command line arguments of the process as a string with 356 // each argument separated by 0x20 ascii character. 357 func (p *Process) Cmdline() (string, error) { 358 return p.CmdlineWithContext(context.Background()) 359 } 360 361 // CmdlineSlice returns the command line arguments of the process as a slice with each 362 // element being an argument. 363 func (p *Process) CmdlineSlice() ([]string, error) { 364 return p.CmdlineSliceWithContext(context.Background()) 365 } 366 367 // Cwd returns current working directory of the process. 368 func (p *Process) Cwd() (string, error) { 369 return p.CwdWithContext(context.Background()) 370 } 371 372 // Parent returns parent Process of the process. 373 func (p *Process) Parent() (*Process, error) { 374 return p.ParentWithContext(context.Background()) 375 } 376 377 // Status returns the process status. 378 // Return value could be one of these. 379 // R: Running S: Sleep T: Stop I: Idle 380 // Z: Zombie W: Wait L: Lock 381 // The character is same within all supported platforms. 382 func (p *Process) Status() (string, error) { 383 return p.StatusWithContext(context.Background()) 384 } 385 386 // Foreground returns true if the process is in foreground, false otherwise. 387 func (p *Process) Foreground() (bool, error) { 388 return p.ForegroundWithContext(context.Background()) 389 } 390 391 // Uids returns user ids of the process as a slice of the int 392 func (p *Process) Uids() ([]int32, error) { 393 return p.UidsWithContext(context.Background()) 394 } 395 396 // Gids returns group ids of the process as a slice of the int 397 func (p *Process) Gids() ([]int32, error) { 398 return p.GidsWithContext(context.Background()) 399 } 400 401 // Terminal returns a terminal which is associated with the process. 402 func (p *Process) Terminal() (string, error) { 403 return p.TerminalWithContext(context.Background()) 404 } 405 406 // Nice returns a nice value (priority). 407 func (p *Process) Nice() (int32, error) { 408 return p.NiceWithContext(context.Background()) 409 } 410 411 // IOnice returns process I/O nice value (priority). 412 func (p *Process) IOnice() (int32, error) { 413 return p.IOniceWithContext(context.Background()) 414 } 415 416 // Rlimit returns Resource Limits. 417 func (p *Process) Rlimit() ([]RlimitStat, error) { 418 return p.RlimitWithContext(context.Background()) 419 } 420 421 // RlimitUsage returns Resource Limits. 422 // If gatherUsed is true, the currently used value will be gathered and added 423 // to the resulting RlimitStat. 424 func (p *Process) RlimitUsage(gatherUsed bool) ([]RlimitStat, error) { 425 return p.RlimitUsageWithContext(context.Background(), gatherUsed) 426 } 427 428 // IOCounters returns IO Counters. 429 func (p *Process) IOCounters() (*IOCountersStat, error) { 430 return p.IOCountersWithContext(context.Background()) 431 } 432 433 // NumCtxSwitches returns the number of the context switches of the process. 434 func (p *Process) NumCtxSwitches() (*NumCtxSwitchesStat, error) { 435 return p.NumCtxSwitchesWithContext(context.Background()) 436 } 437 438 // NumFDs returns the number of File Descriptors used by the process. 439 func (p *Process) NumFDs() (int32, error) { 440 return p.NumFDsWithContext(context.Background()) 441 } 442 443 // NumThreads returns the number of threads used by the process. 444 func (p *Process) NumThreads() (int32, error) { 445 return p.NumThreadsWithContext(context.Background()) 446 } 447 448 func (p *Process) Threads() (map[int32]*cpu.TimesStat, error) { 449 return p.ThreadsWithContext(context.Background()) 450 } 451 452 // Times returns CPU times of the process. 453 func (p *Process) Times() (*cpu.TimesStat, error) { 454 return p.TimesWithContext(context.Background()) 455 } 456 457 // CPUAffinity returns CPU affinity of the process. 458 func (p *Process) CPUAffinity() ([]int32, error) { 459 return p.CPUAffinityWithContext(context.Background()) 460 } 461 462 // MemoryInfo returns generic process memory information, 463 // such as RSS and VMS. 464 func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { 465 return p.MemoryInfoWithContext(context.Background()) 466 } 467 468 // MemoryInfoEx returns platform-specific process memory information. 469 func (p *Process) MemoryInfoEx() (*MemoryInfoExStat, error) { 470 return p.MemoryInfoExWithContext(context.Background()) 471 } 472 473 // PageFaults returns the process's page fault counters. 474 func (p *Process) PageFaults() (*PageFaultsStat, error) { 475 return p.PageFaultsWithContext(context.Background()) 476 } 477 478 // Children returns the children of the process represented as a slice 479 // of pointers to Process type. 480 func (p *Process) Children() ([]*Process, error) { 481 return p.ChildrenWithContext(context.Background()) 482 } 483 484 // OpenFiles returns a slice of OpenFilesStat opend by the process. 485 // OpenFilesStat includes a file path and file descriptor. 486 func (p *Process) OpenFiles() ([]OpenFilesStat, error) { 487 return p.OpenFilesWithContext(context.Background()) 488 } 489 490 // Connections returns a slice of net.ConnectionStat used by the process. 491 // This returns all kind of the connection. This means TCP, UDP or UNIX. 492 func (p *Process) Connections() ([]net.ConnectionStat, error) { 493 return p.ConnectionsWithContext(context.Background()) 494 } 495 496 // ConnectionsMax returns a slice of net.ConnectionStat used by the process at most `max`. 497 func (p *Process) ConnectionsMax(max int) ([]net.ConnectionStat, error) { 498 return p.ConnectionsMaxWithContext(context.Background(), max) 499 } 500 501 // NetIOCounters returns NetIOCounters of the process. 502 func (p *Process) NetIOCounters(pernic bool) ([]net.IOCountersStat, error) { 503 return p.NetIOCountersWithContext(context.Background(), pernic) 504 } 505 506 // MemoryMaps get memory maps from /proc/(pid)/smaps 507 func (p *Process) MemoryMaps(grouped bool) (*[]MemoryMapsStat, error) { 508 return p.MemoryMapsWithContext(context.Background(), grouped) 509 } 510 511 // Tgid returns thread group id of the process. 512 func (p *Process) Tgid() (int32, error) { 513 return p.TgidWithContext(context.Background()) 514 } 515 516 // SendSignal sends a unix.Signal to the process. 517 func (p *Process) SendSignal(sig syscall.Signal) error { 518 return p.SendSignalWithContext(context.Background(), sig) 519 } 520 521 // Suspend sends SIGSTOP to the process. 522 func (p *Process) Suspend() error { 523 return p.SuspendWithContext(context.Background()) 524 } 525 526 // Resume sends SIGCONT to the process. 527 func (p *Process) Resume() error { 528 return p.ResumeWithContext(context.Background()) 529 } 530 531 // Terminate sends SIGTERM to the process. 532 func (p *Process) Terminate() error { 533 return p.TerminateWithContext(context.Background()) 534 } 535 536 // Kill sends SIGKILL to the process. 537 func (p *Process) Kill() error { 538 return p.KillWithContext(context.Background()) 539 } 540 541 // Username returns a username of the process. 542 func (p *Process) Username() (string, error) { 543 return p.UsernameWithContext(context.Background()) 544 } 545 546 // Environ returns the environment variables of the process. 547 func (p *Process) Environ() ([]string, error) { 548 return p.EnvironWithContext(context.Background()) 549 }