Skip to content
Snippets Groups Projects
sysmon_tools.py 15.4 KiB
Newer Older
import psutil
from PyQt4 import Qt, QtCore, QtGui
import datetime
import numpy as np
import config

#check if command line flag --nompl set to disable matplotlib 
if not(config.nompl):
    try:
        import matplotlib
        if matplotlib.get_backend() != 'QT4Agg':
            matplotlib.use('QT4Agg')
        from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
        from matplotlib.backends.backend_qt4 import NavigationToolbar2QT as NavigationToolbar
        import matplotlib.pyplot as plt
        config.mplLoaded=True
    #    print "matplotlib try successful"
    except:
    #    print "matplotlib except case"
        #case where matplotlib not available - need to know this for handling plotting tabs
        config.mplLoaded=False
        pass
else:
    config.mplLoaded=False #use this flag to test case when matplotlib is not available    
    
def constantUpdateActor(self,config):

    #check duration
    Ndur=self.duration

    #update global variables 

    #mode to show status in percentage
    cpu_stats = psutil.cpu_times_percent(interval=0,percpu=False) #syntax seems to be same for psutil versions 1 and 2
    percentcpubusy = 100.0 - cpu_stats.idle
    self.ui.progressBarStatusCPU.setValue(round(percentcpubusy))
    percentmembusy=psutil.virtual_memory().percent
    self.ui.progressBarStatusMemory.setValue(round(percentmembusy))
    Ncpus=len(psutil.cpu_percent(percpu=True))
    self.ui.Ncpus=Ncpus
    totalcpustr='CPU Count: '+str(Ncpus)
#        print "Total CPU str: ",totalcpustr
    self.ui.labelCPUCount.setText(totalcpustr)
    totalmem=int(round(float(psutil.virtual_memory().total)/(1024*1024*1024))) #psutil syntax OK for both versions
#        print "Total Mem: ",totalmem
    self.ui.labelMemUsage.setText('Memory Usage: '+str(totalmem*percentmembusy/100)+' GB')
    self.ui.totalmem=totalmem
    totalmemstr='Max Mem: '+str(totalmem)+' GB'
#        print "Total Mem str: ",totalmemstr
    self.ui.labelMaxMem.setText(totalmemstr)
#    print "** config.mplLoaded: ",config.mplLoaded
    
    if config.mplLoaded:
        #update first position with most recent value overwriting oldest value which has been shifted to first position
        self.ui.cpu=np.roll(self.ui.cpu,1)
        self.ui.cpu[0]=percentcpubusy
        self.ui.mem=np.roll(self.ui.mem,1)
        self.ui.mem[0]=percentmembusy
    #        self.ui.dt=np.roll(self.ui.dt,1)
    #        self.ui.dt[0]=datetime.datetime.now()
    
    # update system tab
    if self.ui.tabWidget.currentIndex() == config.SYST_TAB:
        #determine the computer uptime
        if config.psutilVer == 1:
            uptime = str(datetime.datetime.now() - datetime.datetime.fromtimestamp(psutil.BOOT_TIME))
        else:
            uptime = str(datetime.datetime.now() - datetime.datetime.fromtimestamp(psutil.boot_time()))
        self.ui.labelUptime.setText("System Uptime: "+uptime)


        
        #update the number of users each time interval as well
        userInfo=psutil.get_users() if config.psutilVer == 1 else psutil.users()
        lst=[]
        for item in userInfo:
            lst.append(item.name)
        uusers=set(lst)
        Nuusers=len(uusers)
        self.ui.labelNUsers.setText("Number of Users Logged On: "+str(Nuusers))
    
    #update the history plot
    if self.ui.tabWidget.currentIndex() == config.HIST_TAB:
        #only update history plot if tab is active    
        font = {'family' : 'sans-serif',
            'weight' : 'bold',
            'size'   : 10}
        matplotlib.rc('font', **font)
     
        xtime=range(0,self.ui.Nsamples+1,self.update)
        
        Npts=Ndur/self.update
        
        plt.figure(self.ui.figure.number)     #make plot figure active   
        plt.clf() #clear figure each time for rolling updates to show
        plt.plot(xtime[0:Npts+1],self.ui.cpu[0:Npts+1],color='Blue',label='CPU Busy')
        plt.plot(xtime[0:Npts+1],self.ui.mem[0:Npts+1],color='Green',label='Mem Busy')
        plt.title('Composite CPU and Memory Activity',fontsize=10,fontweight='bold')
        plt.ylabel('% Busy',fontsize=9.5,fontweight='bold')
        
        if self.update == 1:
            xlab="Seconds with 1 Second Updates"
        elif self.update == 2:
            xlab="Seconds with 2 Second Updates"
        elif self.update == 5:
            xlab="Seconds with 5 Second Updates"    
        elif self.update == 10:
            xlab="Seconds with 10 Second Updates"
        plt.xlabel(xlab,fontsize=9.5,fontweight='bold')
        plt.legend(loc="upper right",prop={'size':9})
        plt.xlim([0,Ndur])
        self.ui.canvas.draw()
    
    #update the process table
    if self.ui.tabWidget.currentIndex() == config.PROC_TAB:
        #only update process table if tab is active
        updateProcTable(self,config)
    
    #update the Users bar chart
    if self.ui.tabWidget.currentIndex() == config.USER_TAB:
        #only update bar chart if the tab is active.
        updateUserChart(self,config)
    
    
    
def updateProcTable(self,config):
    if self.doUpdates==True:
        table=self.ui.tableWidgetProcess
        #first remove all rows
        Nrows=table.rowCount()
        for row in range(Nrows):
            table.removeRow(0)
        
        #get the processes
        pidList=psutil.get_pid_list() if config.psutilVer == 1 else psutil.pids()
        Npids=len(pidList)
        
        #now add rows to the table according to the number of processes
    #    for row in range(Npids):
    #        table.insertRow(0)
        
        #now populate the table
        row=0  #table row counter incremented for each row added to the process table
        rout=0 #counter for number of rows to remove due to invalid processes 
        memtot=psutil.virtual_memory()  #psutil syntax OK for both versions
        
        #determine which column has been selected for sorting
        column_sorted=table.horizontalHeader().sortIndicatorSection()
        order = table.horizontalHeader().sortIndicatorOrder()
        #temporarily set sort column to outside column range so that table items can be filled properly
        table.sortItems(5,order=QtCore.Qt.AscendingOrder)
        #print table.horizontalHeader().sortIndicatorSection()
        
        #create empty dictionaries to be used by the process table
        d_user={}
        d_cpu={}
        d_mem={}
        d_name={}

        #fill the dictionaries - seems to need to be done faster than within loop which also fills the table...not sure why...
        for proc in psutil.process_iter():
            cpupct=proc.get_cpu_percent(interval=0) if config.psutilVer == 1 else proc.cpu_percent(interval=0)
            memVal=float(int(float(proc.get_memory_percent())*100.0))/100.0 if config.psutilVer == 1 else float(int(float(proc.memory_percent())*100.0))/100.0
            try:
                #don't update dictionaries if name gives an access denied error when checking process name
                pname=proc.name if config.psutilVer == 1 else proc.name()
                d_user.update({proc.pid:proc.username}) if config.psutilVer == 1 else d_user.update({proc.pid:proc.username()})
                d_cpu.update({proc.pid:cpupct})
                d_mem.update({proc.pid:memVal})
                d_name.update({proc.pid:pname})
            except:
                pass #place holder

        #now fill the table for display
        for proc in d_user.keys():
            #print "proc: ",proc," type: ",type(proc)
            pid=int(proc)
            #print "pid: ",pid

            table.insertRow(0)
            #print "inserting row"
            #set process id
            item=QtGui.QTableWidgetItem()
            item.setData(QtCore.Qt.DisplayRole,pid) 
            table.setItem(0,0,item)
            #set username
            #print " d_user[proc]: ",d_user[proc],"  proc: ",proc
            table.setItem(0,1,QtGui.QTableWidgetItem(d_user[proc]))
            #set CPU %
            item=QtGui.QTableWidgetItem()
            item.setData(QtCore.Qt.DisplayRole,d_cpu[proc])
            table.setItem(0,2,item)
            #set memory %
            item=QtGui.QTableWidgetItem()
            item.setData(QtCore.Qt.DisplayRole,d_mem[proc])
            table.setItem(0,3,item)
            #set process name
            table.setItem(0,4,QtGui.QTableWidgetItem(d_name[proc]))
            row+=1

            
     #   for row in range(rout):
     #       table.removeRow(Npids-row)
        #restore sort to previously selected column
        #table.sortItems(column_sorted,order=QtCore.Qt.AscendingOrder)
        table.sortItems(column_sorted,order=order)
        self.ui.labelLastUpdate.setText("Last Update: "+str(datetime.datetime.now()))

def updateUserChart(self,config):

    font = {'family' : 'sans-serif',
        'weight' : 'bold',
        'size'   : 9}

    matplotlib.rc('font', **font)
    
    f=plt.figure(self.ui.figure2.number)

#    self.ui.figure2=plt.figure(2)
    plt.clf()
    plt.cla()
#    f.gca().cla()

    #create empty dictionaries to be used by the process table
    d_user={}
    d_cpu={}
    d_mem={}
    d_name={}
    #fill the dictionaries - seems to need to be done faster than within loop which also fills the table...not sure why...
    for proc in psutil.process_iter():
        cpupct=proc.get_cpu_percent(interval=0) if config.psutilVer == 1 else proc.cpu_percent(interval=0)
        memVal=float(int(float(proc.get_memory_percent())*100.0))/100.0 if config.psutilVer == 1 else float(int(float(proc.memory_percent())*100.0))/100.0
        try:
            #don't update dictionaries if name gives an access denied error when checking process name
            pname=proc.name if config.psutilVer == 1 else proc.name()
            d_user.update({proc.pid:proc.username}) if config.psutilVer == 1 else d_user.update({proc.pid:proc.username()})
            d_cpu.update({proc.pid:cpupct})
            d_mem.update({proc.pid:memVal})
            d_name.update({proc.pid:pname})
        except:
            pass #place holder
            
    users=d_user.values()
    users_unique=list(set(users)) #use set() to find unique users then convert the resulting set to a list via list()
    Nusers=len(users_unique)

    #create cpu and memory by users dictionaries
    cpu_by_users={}
    mem_by_users={}
    for u in range(Nusers):
        cpu_by_users.update({users_unique[u]:0})
        mem_by_users.update({users_unique[u]:0})
    #apparently update does not order keys in sequence to users_unique
    #thus need to re-create users_unique according to the order of the users
    #in the cpu and mem dictionary keys
    users_unique=list(cpu_by_users.keys())

    #fill cpu and memory dictionaries sequencing thru each PID
    for pid in d_user.keys():
        user=d_user[pid]
        cpu_by_users[user]=cpu_by_users[user] + d_cpu[pid]
        mem_by_users[user]=mem_by_users[user] + d_mem[pid]

    #now convert to a list which we can index
    cpu_by_users_lst=cpu_by_users.values()
    mem_by_users_lst=mem_by_users.values()
        
    width=0.85

    colors=['b','g','r','c','m','y','gray','hotpink','brown','k']
    Nmax=len(colors) #don't want to have more users than colors...

    if self.ui.radioButtonCPU.isChecked():
        sortBy='cpu'
    elif self.ui.radioButtonMem.isChecked():
        sortBy='mem'
    else:
        print "invalid radio button selection - using CPU sort as default"
        sortBy='cpu'
    #sortBy='cpu' # 'cpu' or 'mem' - use for debugging
    #create sort index
    if sortBy=='cpu':
        indx=sorted(range(len(cpu_by_users_lst)), key=cpu_by_users_lst.__getitem__,reverse=True)
    elif sortBy=='mem':
        indx=sorted(range(len(mem_by_users_lst)), key=mem_by_users_lst.__getitem__,reverse=True)
    else:
        print 'Incorrect sort parameter'
    #sort lists
    cpu_by_users_sorted=[cpu_by_users_lst[x] for x in indx]
    mem_by_users_sorted=[mem_by_users_lst[x] for x in indx]
    users_unique_sorted=[users_unique[x] for x in indx]

    #restrict the number of users we'll show to Nmax
    if Nusers > Nmax:
        #replace the Nmaxth - 1 element with the total of the values from index Nmax - 1 to the end of the list
        cpu_by_users_sorted[Nmax-1]=sum(cpu_by_users_sorted[Nmax-1:])
        mem_remaining=sum(mem_by_users_sorted[Nmax-1:])
        users_unique_sorted[Nmax-1]='Remaining'
        Nshow=Nmax
    else:
        Nshow=Nusers

    if min(cpu_by_users_sorted) < 0:
        print " *** cp_by_users_sorted has values less than zero"
        print cpu_by_users_sorted
        print " ***"
    if min(mem_by_users_sorted) < 0:
        print " *** mem_by_users_sorted has values less than zero"
        print mem_by_users_sorted
        print " ***"        

    #range check the values of the sorted lists - may not be necessary, just being cautious...
    tst=np.array(cpu_by_users_sorted)<0  #need an array for summing
    indx=cpu_by_users_sorted<0           #need bool list for indexing
    if sum(tst) > 0:
        print "cpu_by_users < 0: ",sum(indx)
        cpu_by_users_sorted[indx]=0
    tst=np.array(cpu_by_users_sorted)>self.ui.Ncpus*100
    indx=cpu_by_users_sorted>self.ui.Ncpus*100
    if sum(tst) > 0:
        print "cpu_by_users > Ncpus*100: ",sum(indx)
        cpu_by_users_sorted[indx]=self.ui.Ncpus*100
    tst=np.array(mem_by_users_sorted)<0
    indx=mem_by_users_sorted<0
    if sum(tst) > 0:
        print "mem_by_users < 0: ",sum(indx)
        mem_by_users_sorted[indx]=0
    tst=np.array(mem_by_users_sorted)>self.ui.totalmem
    indx=mem_by_users_sorted>self.ui.totalmem
    if sum(tst) > 0:
        print "mem_by_users > totalmem: ",sum(indx)
        mem_by_users_sorted[indx]=self.ui.totalmem
    
    
    p=[] #list to contain plot objects for use by the legend   
    ind=np.arange(2)
    
    
    for u in range(Nshow):
        if u == 0:
            p.append(plt.bar(ind,(cpu_by_users_sorted[u],mem_by_users_sorted[u]),width,bottom=(0,0),color=colors[u]))
        else:
            p.append(plt.bar(ind,(cpu_by_users_sorted[u],mem_by_users_sorted[u]),width,bottom=(sum(cpu_by_users_sorted[0:u]),sum(mem_by_users_sorted[0:u])),color=colors[u]))
    
    plt.title('CPU and Memory Usage by User',fontsize=10,fontweight='bold')
    
    #remove default yaxis ticks then redraw them via ax1 and ax2 below
    frame=plt.gca()
    frame.axes.get_yaxis().set_ticks([])
    plt.xticks(np.arange(2)+width/2.,('CPU','Mem'),fontsize=9,fontweight='bold')
    ymaxCPU=round(int((sum(cpu_by_users_sorted[0:Nshow])+100)/100)*100)
    ymaxMEM=round(int((sum(mem_by_users_sorted[0:Nshow])+100)/100)*100)
    ymax=max([ymaxCPU,ymaxMEM,100])
#    plt.ylim([0,ymax])
    
    #compute composite %
    ylab=np.arange(6)/5.0*float(ymax)/float(self.ui.Ncpus)
    ylab=ylab*10
    tmp=ylab.astype('int')
    tmp1=tmp.astype('float')
    tmp1=tmp1/10
    ylab1=tmp1
    
    ax1=plt.twinx()
    ax1.set_ylabel('Composite CPU Percent',fontsize=9,fontweight='bold')
    ax1.yaxis.set_ticks_position('left')
    ax1.yaxis.set_label_position('left')
    ax1.set_yticks(ylab1)
    
    usersLegend=users_unique_sorted[0:Nshow]
    #reverse legend print order to have legend correspond with the order data are placed in the bar chart
    usersLegend.reverse()
    p.reverse()
    plt.legend(p,usersLegend)

    
    #place second y axis label on plot
    ylab2=np.arange(5)/4.0*float(ymax)
    ax2=plt.twinx()
    ax2.set_ylabel('Percent',fontsize=9,fontweight='bold')
    ax2.set_yticks(ylab2)
    #ax2.set_yticks(ylab2)
    ax2.yaxis.set_ticks_position('right')
    ax2.yaxis.set_label_position('right')

    self.ui.canvas2.draw()
    
def reLayout(self):
    tmpWidget=QtGui.QWidget()
    tmpWidget.setLayout(self.ui.layout2)
    tmpLayout = QtGui.QVBoxLayout(self.ui.frameBar)