#!/usr/bin/env python
#
# fdprogress.py -- by Alfe (alfe@alfe.de), inspired by azat@Stackoverflow
#
# usage: fdprogress.py
#
import time, os, os.path
from collections import defaultdict
def getFds(pid):
return os.listdir('/proc/%s/fd/' % pid)
def getPos(pid, fd):
with open('/proc/%s/fdinfo/%s' % (pid, fd)) as f:
return int(f.readline()[5:])
def getSize(pid, fd):
return os.path.getsize(getPath(pid, fd))
class FdIsPipe(Exception): pass
def getPath(pid, fd):
result = os.readlink('/proc/%s/fd/%s' % (pid, fd))
if result.startswith('pipe:['):
raise FdIsPipe(result)
return result
def extendHistory(history, pid):
for fd in getFds(pid):
try:
history[fd, getPath(pid, fd)].append(
(time.time(), getPos(pid, fd), getSize(pid, fd)))
except FdIsPipe:
pass # ignore fds to pipe
def initHistory(pid):
result = defaultdict(list)
extendHistory(result, pid)
return result
def reduceHistory(history):
for key, value in history.iteritems():
if len(value) > 2:
del value[1:-2] # only keep first and last
# (this can be more clever in the future)
def entryPrediction(fd, path, values):
t1, pos1, size1 = values[0]
t2, pos2, size2 = values[-1]
if t1 == t2: # no time passed yet?
return fd, path, (t2, pos2, size2), None, None, None, None, None, None, None
growth = (size2 - size1) / (t2 - t1) # bytes/sec growth of file
if growth != 0:
tSize0 = t1 - size1 / growth # time when size was 0
else:
tSize0 = None
speed = (pos2 - pos1) / (t2 - t1) # speed of pos in bytes/sec
if speed != 0:
tPos0 = t1 - pos1 / speed # time when pos was 0
tPosSize2 = t1 + (size2 - pos1) / speed # time of pos reaching size2
else:
tPos0 = tPosSize2 = None
if speed != growth: # when will both meet?
tm = t2 + (size2 - pos2) / (speed - growth)
sizeM = size2 + growth * (tm - t2)
else:
tm = sizeM = None
return (fd, path, (t2, pos2, size2), growth, speed, tSize0, tPos0,
tPosSize2, tm, sizeM)
def eachPrediction(history):
for (fd, path), values in history.iteritems():
yield entryPrediction(fd, path, values)
def displayTime(t):
if t is None:
return "<>"
d = t - time.time()
try:
lt = time.localtime(t)
except:
return "??"
return (
time.strftime("%%F (now%+dy)" % (d/86400/365), lt)
if abs(d) > 2 * 86400 * 365 else
time.strftime("%%F (now%+dM)" % (d/86400/30), lt)
if abs(d) > 2 * 86400 * 30 else
time.strftime("%%F (now%+dd)" % (d/86400), lt)
if abs(d) > 2 * 86400 else
time.strftime("%%a, %%T (now%+dh)" % (d/3600), lt)
if time.strftime('%F', lt) != time.strftime('%F', time.localtime()) else
time.strftime("%%T (now%+dh)" % (d/3600), lt)
if abs(d) > 2 * 3600 else
time.strftime("%%T (now%+dm)" % (d/60), lt)
if abs(d) > 2 * 60 else
time.strftime("%%T (now%+ds)" % d, lt))
def displaySize(size):
return (
"<>" if size is None else
"%d B" % size
if size < 1e3 else
"%.2f kB" % (size / 1e3)
if size < 1e6 else
"%.2f MB" % (size / 1e6)
if size < 1e9 else
"%.2f GB" % (size / 1e9))
def displaySpeed(speed):
return displaySize(speed) + "/s"
def printPrediction(history):
for (fd, path, (t2, pos2, size2), growth, speed, tSize0, tPos0,
tPosSize2, tm, sizeM) in eachPrediction(history):
print '\n', fd, "->", os.path.basename(path)
dT = displayTime
dSi = displaySize
dSp = displaySpeed
print "size:", dSi(size2), "\tgrowth:", dSp(growth), \
"\t\tpos:", dSi(pos2), "\tspeed:", dSp(speed)
print "emptyTime:", dT(tSize0), "\tstartTime:", dT(tPos0), \
"\treachTime:", dT(tPosSize2), "\tmeetTime:", dT(tm)
def main(argv):
pid = argv[1]
history = initHistory(pid)
while True:
os.system('clear')
printPrediction(history)
extendHistory(history, pid)
reduceHistory(history)
time.sleep(1.0)
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))