#!/usr/bin/env python

# Matthieu Brucher
# Last Change : 2008-11-19 19:05

import subprocess
import threading
import datetime

names = [("pid", int),
         ("comm", str),
         ("state", str),
         ("ppid", int),
         ("pgrp", int),
         ("session", int),
         ("tty_nr", int),
         ("tpgid", int),
         ("flags", int),
         ("minflt", int),
         ("cminflt", int),
         ("majflt", int),
         ("cmajflt", int),
         ("utime", int),
         ("stime", int),
         ("cutime", int),
         ("cstime", int),
         ("priority", int),
         ("nice", int),
         ("0", int),
         ("itrealvalue", int),
         ("starttime", int),
         ("vsize", int),
         ("rss", int),
         ("rlim", int),
         ("startcode", int),
         ("endcode", int),
         ("startstack", int),
         ("kstkesp", int),
         ("kstkeip", int),
         ("signal", int),
         ("blocked", int),
         ("sigignore", int),
         ("sigcatch", int),
         ("wchan", int),
         ("nswap", int),
         ("cnswap", int),
         ("exit_signal", int),
         ("processor", int),]

colours = ['b', 'g', 'r', 'c', 'm', 'y']

def getPageSize():
  import resource
  f = open("/proc/meminfo")
  mem = f.readline()
  f.close()
  return resource.getpagesize() / (1024 * float(mem[10:-3].strip()))

pagesizepercent = getPageSize()

def collectData(pid, task):
  """
  Collect process list
  """
  f1 = open("/proc/%d/task/%s/stat"%(pid,task))
  f2 = open("/proc/%d/task/%s/statm"%(pid,task))
  t = datetime.datetime.now()
  stat = f1.readline().split()
  mem = f2.readline().split()
  d = dict([(name[0], name[1](el)) for (name, el) in zip(names, stat)])
  d["pmem"] = 100 * float(mem[1]) * pagesizepercent
  return t, d

def getTime(key):
  """
  Returns the time in microseconds
  """
  return (((key.weekday() * 24 + key.hour) * 60 + key.minute) * 60 + key.second) * 1000000 + key.microsecond
  
class MonitorThread(threading.Thread):
  """
  The monitor thread saves the process info every 5 seconds
  """
  def __init__(self, pid):
    import collections

    self.pid = pid
    threading.Thread.__init__(self)
    self.data = collections.defaultdict(dict)
    self.process = True
    
  def run(self):
    import os
    import time

    while self.process:
      threads = os.listdir("/proc/%d/task/" % self.pid)
      for thread in threads:
        t, d = collectData(self.pid, thread)
        d["current_time"] = t
        
        if "now" in self.data[thread]:
          now = self.data[thread]["now"]
          d['pcpu'] = 1e6 * ((d['utime'] + d['stime']) - (now['utime'] + now['stime'])) / float((getTime(t) - getTime(now["current_time"])))

        self.data[thread][getTime(t)] = d
        self.data[thread]["now"] = d
      time.sleep(1)

def displayCPU(data, pid):
  """
  Displays and saves the graph
  """
  import pylab
  import numpy
  
  spid = str(pid)
  
  c = 0
  threads = data.keys()
  threads.sort()
  for thread in threads:
    d = data[thread]
    keys = d.keys()
    keys.remove("now")
    keys.sort()
    mykeys = numpy.array(keys)/1e6
    mykeys -= mykeys[0]
  
    pylab.plot(mykeys[2:], [d[key]['pcpu'] for key in keys[2:]], colours[c], label = thread)
    c = c+1
    if spid == thread:
      pylab.plot(mykeys[2:], [d[key]['pmem'] for key in keys[2:]], 'k', label = 'MEM')

  pylab.ylim([-5, 105])
  pylab.legend(loc=6)
  
  pylab.savefig('%d.svg' % pid)
  pylab.savefig('%d.png' % pid)
  pylab.close()

if __name__ == "__main__":
  import sys
  import os
  import pickle
  
  stdin = open(sys.argv[1])
  stdout = open(sys.argv[2], "w")
  
  process = subprocess.Popen(sys.argv[3:], stdin = stdin, stdout = stdout)
  
  thread = MonitorThread(process.pid)
  thread.start()

  process.wait()

  thread.process = False
  thread.join()
  
  f = open('%d.data' % process.pid, 'w')
  pickle.dump(thread.data, f)
  
  try:
    displayCPU(thread.data, process.pid)
  except:
    pass
