[svn-upgrade] Integrating new upstream version, cpulimit (1.12~svn10)
[debian/cpulimit.git] / process.c
1 /**
2  *
3  * cpulimit - a cpu limiter for Linux
4  *
5  * Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20  */
21
22 //TODO: add documentation to public functions
23
24 #include "process.h"
25
26 int process_init(struct process_history *proc, int pid)
27 {
28         proc->pid = pid;
29         //init circular queue
30         proc->front_index = -1;
31         proc->tail_index = 0;
32         proc->actual_history_size = 0;
33         memset(proc->history, 0, sizeof(proc->history));
34         proc->total_workingtime = 0;
35         //test /proc file descriptor for reading
36         sprintf(proc->stat_file, "/proc/%d/stat", proc->pid);
37         FILE *fd = fopen(proc->stat_file, "r");
38         fclose(fd);
39         proc->usage.pcpu = 0;
40         proc->usage.workingrate = 0;
41         return (fd == NULL);
42 }
43
44 //return t1-t2 in microseconds (no overflow checks, so better watch out!)
45 static inline unsigned long timediff(const struct timespec *t1,const struct timespec *t2)
46 {
47         return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_nsec/1000 - t2->tv_nsec/1000);
48 }
49
50 static int get_jiffies(struct process_history *proc) {
51         FILE *f = fopen(proc->stat_file, "r");
52         if (f==NULL) return -1;
53         fgets(proc->buffer, sizeof(proc->buffer),f);
54         fclose(f);
55         char *p = proc->buffer;
56         p = memchr(p+1,')', sizeof(proc->buffer) - (p-proc->buffer));
57         int sp = 12;
58         while (sp--)
59                 p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer));
60         //user mode jiffies
61         int utime = atoi(p+1);
62         p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer));
63         //kernel mode jiffies
64         int ktime = atoi(p+1);
65         return utime+ktime;
66 }
67
68 int process_monitor(struct process_history *proc, int last_working_quantum, struct cpu_usage *usage)
69 {
70         //increment front index
71         proc->front_index = (proc->front_index+1) % HISTORY_SIZE;
72
73         //actual history size is: (front-tail+HISTORY_SIZE)%HISTORY_SIZE+1
74         proc->actual_history_size = (proc->front_index - proc->tail_index + HISTORY_SIZE) % HISTORY_SIZE + 1;
75
76         //actual front and tail of the queue
77         struct process_screenshot *front = &(proc->history[proc->front_index]);
78         struct process_screenshot *tail = &(proc->history[proc->tail_index]);
79
80         //take a shot and save in front
81         int j = get_jiffies(proc);
82         if (j<0) return -1; //error retrieving jiffies count (maybe the process is dead)
83         front->jiffies = j;
84         clock_gettime(CLOCK_REALTIME, &(front->when));
85         front->cputime = last_working_quantum;
86
87         if (proc->actual_history_size==1) {
88                 //not enough elements taken (it's the first one!), return 0
89                 usage->pcpu = -1;
90                 usage->workingrate = 1;
91                 return 0;
92         }
93         else {
94                 //queue has enough elements
95                 //now we can calculate cpu usage, interval dt and dtwork are expressed in microseconds
96                 long dt = timediff(&(front->when), &(tail->when));
97                 //the total time between tail and front in which the process was allowed to run
98                 proc->total_workingtime += front->cputime - tail->cputime;
99                 int used_jiffies = front->jiffies - tail->jiffies;
100                 float usage_ratio = (used_jiffies*1000000.0/HZ) / proc->total_workingtime;
101                 usage->workingrate = 1.0 * proc->total_workingtime / dt;
102                 usage->pcpu = usage_ratio * usage->workingrate;
103                 //increment tail index if the queue is full
104                 if (proc->actual_history_size==HISTORY_SIZE)
105                         proc->tail_index = (proc->tail_index+1) % HISTORY_SIZE;
106                 return 0;
107         }
108 }
109
110 int process_close(struct process_history *proc)
111 {
112         if (kill(proc->pid,SIGCONT)!=0) {
113                 fprintf(stderr,"Process %d is already dead!\n", proc->pid);
114         }
115         proc->pid = 0;
116         return 0;
117 }