How to retrieve the process start time (or uptime) in python
Asked Answered
G

7

43

How to retrieve the process start time (or uptime) in python in Linux?

I only know, I can call "ps -p my_process_id -f" and then parse the output. But it is not cool.

Gynecologist answered 8/4, 2010 at 7:11 Comment(2)
I have found an anwser: ps -p YOUR_PID -o lstart=Gynecologist
yes, the command line tool ps can be used. But why not use a library?Andaman
L
28

If you are doing it from within the python program you're trying to measure, you could do something like this:

import time
# at the beginning of the script
startTime = time.time()
# ...
def getUptime():
    """
    Returns the number of seconds since the program started.
    """
    # do return startTime if you just want the process start time
    return time.time() - startTime

Otherwise, you have no choice but to parse ps or go into /proc/pid. A nice bashy way of getting the elapsed time is:

ps -eo pid,etime | grep $YOUR_PID | awk '{print $2}'

This will only print the elapsed time in the following format, so it should be quite easy to parse:

days-HH:MM:SS

(if it's been running for less than a day, it's just HH:MM:SS)

The start time is available like this:

ps -eo pid,stime | grep $YOUR_PID | awk '{print $2}'

Unfortunately, if your process didn't start today, this will only give you the date that it started, rather than the time.

The best way of doing this is to get the elapsed time and the current time and just do a bit of math. The following is a python script that takes a PID as an argument and does the above for you, printing out the start date and time of the process:

import sys
import datetime
import time
import subprocess

# call like this: python startTime.py $PID

pid = sys.argv[1]
proc = subprocess.Popen(['ps','-eo','pid,etime'], stdout=subprocess.PIPE)
# get data from stdout
proc.wait()
results = proc.stdout.readlines()
# parse data (should only be one)
for result in results:
    try:
        result.strip()
        if result.split()[0] == pid:
            pidInfo = result.split()[1]
            # stop after the first one we find
            break
    except IndexError:
        pass # ignore it
else:
    # didn't find one
    print "Process PID", pid, "doesn't seem to exist!"
    sys.exit(0)
pidInfo = [result.split()[1] for result in results
           if result.split()[0] == pid][0]
pidInfo = pidInfo.partition("-")
if pidInfo[1] == '-':
    # there is a day
    days = int(pidInfo[0])
    rest = pidInfo[2].split(":")
    hours = int(rest[0])
    minutes = int(rest[1])
    seconds = int(rest[2])
else:
    days = 0
    rest = pidInfo[0].split(":")
    if len(rest) == 3:
        hours = int(rest[0])
        minutes = int(rest[1])
        seconds = int(rest[2])
    elif len(rest) == 2:
        hours = 0
        minutes = int(rest[0])
        seconds = int(rest[1])
    else:
        hours = 0
        minutes = 0
        seconds = int(rest[0])

# get the start time
secondsSinceStart = days*24*3600 + hours*3600 + minutes*60 + seconds
# unix time (in seconds) of start
startTime = time.time() - secondsSinceStart
# final result
print "Process started on",
print datetime.datetime.fromtimestamp(startTime).strftime("%a %b %d at %I:%M:%S %p")
Lucrece answered 8/4, 2010 at 7:23 Comment(0)
N
83

By using psutil https://github.com/giampaolo/psutil:

>>> import psutil, os, time
>>> p = psutil.Process(os.getpid())
>>> p.create_time()
1293678383.0799999
>>> time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(p.create_time()))
'2010-12-30 04:06:23'
>>>

...plus it's cross platform, not only Linux.

NB: I am one of the authors of this project.

Numinous answered 30/12, 2010 at 3:9 Comment(4)
citing the FAQ ( stackoverflow.com/faq#promotion ) "you must disclose your affiliation in your answers."Dysgenics
It's not clear what is meant by "disclose" though. What am I supposed to do in such case?Lated
You should just state that you are involved with the project (nice project btw)Dysgenics
Thanks, this is really slick, simple, and cross-platform: exactly what I was looking for!Unhandy
L
28

If you are doing it from within the python program you're trying to measure, you could do something like this:

import time
# at the beginning of the script
startTime = time.time()
# ...
def getUptime():
    """
    Returns the number of seconds since the program started.
    """
    # do return startTime if you just want the process start time
    return time.time() - startTime

Otherwise, you have no choice but to parse ps or go into /proc/pid. A nice bashy way of getting the elapsed time is:

ps -eo pid,etime | grep $YOUR_PID | awk '{print $2}'

This will only print the elapsed time in the following format, so it should be quite easy to parse:

days-HH:MM:SS

(if it's been running for less than a day, it's just HH:MM:SS)

The start time is available like this:

ps -eo pid,stime | grep $YOUR_PID | awk '{print $2}'

Unfortunately, if your process didn't start today, this will only give you the date that it started, rather than the time.

The best way of doing this is to get the elapsed time and the current time and just do a bit of math. The following is a python script that takes a PID as an argument and does the above for you, printing out the start date and time of the process:

import sys
import datetime
import time
import subprocess

# call like this: python startTime.py $PID

pid = sys.argv[1]
proc = subprocess.Popen(['ps','-eo','pid,etime'], stdout=subprocess.PIPE)
# get data from stdout
proc.wait()
results = proc.stdout.readlines()
# parse data (should only be one)
for result in results:
    try:
        result.strip()
        if result.split()[0] == pid:
            pidInfo = result.split()[1]
            # stop after the first one we find
            break
    except IndexError:
        pass # ignore it
else:
    # didn't find one
    print "Process PID", pid, "doesn't seem to exist!"
    sys.exit(0)
pidInfo = [result.split()[1] for result in results
           if result.split()[0] == pid][0]
pidInfo = pidInfo.partition("-")
if pidInfo[1] == '-':
    # there is a day
    days = int(pidInfo[0])
    rest = pidInfo[2].split(":")
    hours = int(rest[0])
    minutes = int(rest[1])
    seconds = int(rest[2])
else:
    days = 0
    rest = pidInfo[0].split(":")
    if len(rest) == 3:
        hours = int(rest[0])
        minutes = int(rest[1])
        seconds = int(rest[2])
    elif len(rest) == 2:
        hours = 0
        minutes = int(rest[0])
        seconds = int(rest[1])
    else:
        hours = 0
        minutes = 0
        seconds = int(rest[0])

# get the start time
secondsSinceStart = days*24*3600 + hours*3600 + minutes*60 + seconds
# unix time (in seconds) of start
startTime = time.time() - secondsSinceStart
# final result
print "Process started on",
print datetime.datetime.fromtimestamp(startTime).strftime("%a %b %d at %I:%M:%S %p")
Lucrece answered 8/4, 2010 at 7:23 Comment(0)
B
17

man proc says that the 22nd item in /proc/my_process_id/stat is:

starttime %lu

The time in jiffies the process started after system boot.

Your problem now is, how to determine the length of a jiffy and how to determine when the system booted.

The answer for the latter comes still from man proc: it's in /proc/stat, on a line of its own like this:

btime 1270710844

That's a measurement in seconds since Epoch.


The answer for the former I'm not sure about. man 7 time says:

The Software Clock, HZ, and Jiffies

The accuracy of many system calls and timestamps is limited by the resolution of the software clock, a clock maintained by the kernel which measures time in jiffies. The size of a jiffy is determined by the value of the kernel constant HZ. The value of HZ varies across kernel versions and hardware platforms. On x86 the situation is as follows: on kernels up to and including 2.4.x, HZ was 100, giving a jiffy value of 0.01 seconds; starting with 2.6.0, HZ was raised to 1000, giving a jiffy of 0.001 seconds; since kernel 2.6.13, the HZ value is a kernel configuration parameter and can be 100, 250 (the default) or 1000, yielding a jiffies value of, respectively, 0.01, 0.004, or 0.001 seconds.

We need to find HZ, but I have no idea on how I'd go about that from Python except for hoping the value is 250 (as Wikipedia claims is the default).

ps obtains it thus:

  /* sysinfo.c init_libproc() */
  if(linux_version_code > LINUX_VERSION(2, 4, 0)){ 
    Hertz = find_elf_note(AT_CLKTCK);
    //error handling
  }
  old_Hertz_hack(); //ugh

This sounds like a job well done by a very small C module for Python :)

Bunker answered 8/4, 2010 at 7:43 Comment(2)
I got this far too! :D +1 for your effort!Axseed
See https://mcmap.net/q/378091/-how-to-retrieve-the-process-start-time-or-uptime-in-python for a working solution.Amative
O
8

Here's code based on badp's answer:

import os
from time import time

HZ = os.sysconf(os.sysconf_names['SC_CLK_TCK'])

def proc_age_secs():
    system_stats = open('/proc/stat').readlines()
    process_stats = open('/proc/self/stat').read().split()
    for line in system_stats:
        if line.startswith('btime'):
            boot_timestamp = int(line.split()[1])
    age_from_boot_jiffies = int(process_stats[21])
    age_from_boot_timestamp = age_from_boot_jiffies / HZ
    age_timestamp = boot_timestamp + age_from_boot_timestamp
    return time() - age_timestamp

I'm not sure if it's right though. I wrote a test program that calls sleep(5) and then runs it and the output is wrong and varies over a couple of seconds from run to run. This is in a vmware workstation vm:

if __name__ == '__main__':
    from time import sleep
    sleep(5)
    print proc_age_secs()

The output is:

$ time python test.py
6.19169998169

real    0m5.063s
user    0m0.020s
sys     0m0.036s
Originate answered 23/12, 2011 at 14:46 Comment(1)
See https://mcmap.net/q/378091/-how-to-retrieve-the-process-start-time-or-uptime-in-python for a simpler approach based on this answer.Amative
P
3
def proc_starttime(pid=os.getpid()):
    # https://gist.github.com/westhood/1073585
    p = re.compile(r"^btime (\d+)$", re.MULTILINE)
    with open("/proc/stat") as f:
        m = p.search(f.read())
    btime = int(m.groups()[0])

    clk_tck = os.sysconf(os.sysconf_names["SC_CLK_TCK"])
    with open("/proc/%d/stat" % pid) as f:
        stime = int(f.read().split()[21]) / clk_tck

    return datetime.fromtimestamp(btime + stime)
Parsimony answered 28/7, 2015 at 13:0 Comment(0)
A
3

This implementation is based on Dan Benamy's answer and simplified by using the uptime in seconds instead of the boot time in seconds since the epoch. Approach was tested with both Python 2 and 3.

import os
import time


def processRealTimeSeconds():
    uptimeSeconds = float(open("/proc/uptime").readline().split()[0])
    procStartJif = float(open("/proc/self/stat").readline().split()[21])

    clockTicks = os.sysconf(os.sysconf_names["SC_CLK_TCK"])
    procStartSecondsBoot = procStartJif / clockTicks

    return uptimeSeconds - procStartSecondsBoot


if __name__ == '__main__':
    time.sleep(5)
    print("Real process time: {} s".format(processRealTimeSeconds()))

Output:

$ time ./processTime.py 
Real process time: 5.060000000012224 s

real    0m5.059s
user    0m0.041s
sys     0m0.013s

Addendum: You can't expect millisecond accuracy from this code. Apart from the limited resolution of the timestamps provided by the /proc interfaces, there's always a possibility that the execution is interrupted by task switches. However it can probably be improved by splitting the code into reading the raw data first, then doing the conversions and calculations, to the expense of less concise code with more lines.

Amative answered 26/7, 2023 at 13:35 Comment(2)
Since /proc/uptime is in precision of .01 seconds or 10ms time.clock_gettime(CLOCK_BOOTTIME) can be used to get a more precise time-since-boot.Mufti
@Mufti Note that time.clock_gettime() was introduced in Python 3.3, time.CLOCK_BOOTTIME in 3.7. This approach works also with 2.7 (and probably before), which some still have to support. For my specific solution the precision is good enough.Amative
W
0

you can parse /proc/uptime

>>> uptime, idletime = [float(f) for f in open("/proc/uptime").read().split()]
>>> print uptime
29708.1
>>> print idletime
26484.45

for windows machines, you can probably use wmi

import wmi
c = wmi.WMI()
secs_up = int([uptime.SystemUpTime for uptime in c.Win32_PerfFormattedData_PerfOS_System()][0])
hours_up = secs_up / 3600
print hours_up
Whitethroat answered 8/4, 2010 at 7:41 Comment(2)
Process, not system start time.Bunker
OP also stated or uptime. So this is a solution for uptime.Whitethroat

© 2022 - 2024 — McMap. All rights reserved.