github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/client/internal/monitoring/resource_usage_fetcher.go (about)

     1  // Copyright 2017 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build windows || darwin
    16  
    17  package monitoring
    18  
    19  import (
    20  	"fmt"
    21  	"os/exec"
    22  	"time"
    23  
    24  	"github.com/shirou/gopsutil/process"
    25  )
    26  
    27  // TODO
    28  
    29  // ResourceUsage contains resource-usage data for a single process.
    30  type ResourceUsage struct {
    31  	// When the resource-usage data was retrieved.
    32  	Timestamp time.Time
    33  
    34  	// Amount of CPU time scheduled for a process so far in user mode.
    35  	UserCPUMillis float64
    36  
    37  	// Amount of CPU time scheduled for a process so far in kernel mode.
    38  	SystemCPUMillis float64
    39  
    40  	// Resident set size for a process, in bytes.
    41  	ResidentMemory int64
    42  
    43  	// Number of open file descriptors.
    44  	NumFDs int32
    45  }
    46  
    47  // ResourceUsageFetcher obtains resource-usage data for a process from the OS.
    48  type ResourceUsageFetcher struct{}
    49  
    50  // ResourceUsageFromFinishedCmd returns a ResourceUsage struct with
    51  // information from exec.Cmd.ProcessState. NOTE that this is only possible
    52  // after the process has finished and has been waited for, and will most
    53  // probably panic otherwise.
    54  // This function doesn't fill in ResourceUsage.ResidentMemory.
    55  func (f ResourceUsageFetcher) ResourceUsageFromFinishedCmd(cmd *exec.Cmd) *ResourceUsage {
    56  	return &ResourceUsage{
    57  		Timestamp:       time.Now(),
    58  		UserCPUMillis:   float64(cmd.ProcessState.UserTime().Nanoseconds()) / 1e6,
    59  		SystemCPUMillis: float64(cmd.ProcessState.SystemTime().Nanoseconds()) / 1e6,
    60  	}
    61  }
    62  
    63  // ResourceUsageForPID returns a ResourceUsage struct with information
    64  // from /proc/<PID>/stat and /proc/<PID>/statm . This is only possible with running processes,
    65  // an error will be returned if the corresponding procfs entry is not present.
    66  func (f ResourceUsageFetcher) ResourceUsageForPID(pid int) (*ResourceUsage, error) {
    67  	timestamp := time.Now()
    68  
    69  	process, err := process.NewProcess(int32(pid))
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	memoryInfo, err := process.MemoryInfo()
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	times, err := process.Times()
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	return &ResourceUsage{
    85  		Timestamp:       timestamp,
    86  		UserCPUMillis:   times.User * 1e3,
    87  		SystemCPUMillis: times.System * 1e3,
    88  		ResidentMemory:  int64(memoryInfo.RSS),
    89  	}, nil
    90  }
    91  
    92  // DebugStatusForPID returns a string containing extra debug info about resource-usage that may not be
    93  // captured in ResourceUsage. This is only possible for running processes.
    94  func (f ResourceUsageFetcher) DebugStatusForPID(pid int) (string, error) {
    95  	process, err := process.NewProcess(int32(pid))
    96  	if err != nil {
    97  		return "", err
    98  	}
    99  
   100  	memoryInfo, err := process.MemoryInfo()
   101  	if err != nil {
   102  		return "", err
   103  	}
   104  
   105  	times, err := process.Times()
   106  	if err != nil {
   107  		return "", err
   108  	}
   109  
   110  	return fmt.Sprintf(`
   111  Process:
   112  %q
   113  
   114  Times:
   115  %q
   116  
   117  MemoryInfo:
   118  %q`, process, times, memoryInfo), nil
   119  }