8

I'm using Linux, go, and os/exec to run some commands. I want to know a process' realtime memory usage. That means that I can ask for memory usage anytime after I start the process, not just after it ran.

(That's why the answer in Measuring memory usage of executable run using golang is not an option for me)

For example:

cmd := exec.Command(...)
cmd.Start()
//...
if cmd.Memory()>50 { 
    fmt.Println("Oh my god, this process is hungry for memory!")
}

I don't need very precise value, but it would be great if it's error range is lower than, say, 10 megabytes.

Is there a go way to do that or I need some kind of command line trick?

Community
  • 1
  • 1
mraron
  • 2,411
  • 18
  • 27
  • 1
    You have to look up the memory usage via the OS's facilities. You have the PID already, that's all you should need. – JimB Aug 07 '15 at 14:22
  • [This answer](http://stackoverflow.com/questions/131303/how-to-measure-actual-memory-usage-of-an-application-or-process) is very relevant. You could start by using `htop` or running a script which will monitor a single process. I would also consider this [dtrace for linux](https://github.com/dtrace4linux/linux). – patm Aug 07 '15 at 14:34
  • 1
    Is there a reason not use ulimit or some other third-party process-limiting service? – Jonathan Hall Aug 07 '15 at 16:32
  • @JimB Ok, so there's no go-standard way. Thanks! :) – mraron Aug 07 '15 at 17:37
  • @atmosx Thanks for the answer link, it was very helpful! However the dtrace seems a bit overkill for me! :) – mraron Aug 07 '15 at 17:41
  • 1
    @Filmzy Sorry for confusion, but I don't want to kill the process if it's memory usage is bigger than some bound, I just want to measure its memory usage. My bad, it was a bad example. I will edit it :) – mraron Aug 07 '15 at 17:41

1 Answers1

7

Here is what I use on Linux:

func calculateMemory(pid int) (uint64, error) {

    f, err := os.Open(fmt.Sprintf("/proc/%d/smaps", pid))
    if err != nil {
        return 0, err
    }
    defer f.Close()

    res := uint64(0)
    pfx := []byte("Pss:")
    r := bufio.NewScanner(f)
    for r.Scan() {
        line := r.Bytes()
        if bytes.HasPrefix(line, pfx) {
            var size uint64
            _, err := fmt.Sscanf(string(line[4:]), "%d", &size)
            if err != nil {
                return 0, err
            }
            res += size
        }
    }
    if err := r.Err(); err != nil {
        return 0, err
    }

    return res, nil
}

This function returns the PSS (Proportional Set Size) for a given PID, expressed in KB. If you have just started the process, you should have the rights to access the corresponding /proc file.

Tested with kernel 3.0.13.

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
Didier Spezia
  • 70,911
  • 12
  • 189
  • 154