[svn-upgrade] Integrating new upstream version, cpulimit (1.12~svn10)
[debian/cpulimit.git] / cpulimit.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  *
23  * This is a simple program to limit the cpu usage of a process
24  * If you modify this code, send me a copy please
25  *
26  * Date:    15/2/2008
27  * Version: 1.2 alpha
28  * Get the latest version at: http://cpulimit.sourceforge.net
29  *
30  * Changelog:
31  * - reorganization of the code, splitted in more source files
32  * - control function process_monitor() optimized by eliminating an unnecessary loop
33  * - experimental support for multiple control of children processes and threads
34  *   children detection algorithm seems heavy because of the amount of code,
35  *   but it's designed to be scalable when there are a lot of children processes
36  * - cpu count detection, i.e. if you have 4 cpu, it is possible to limit up to 400%
37  * - in order to avoid deadlock, cpulimit prevents to limit itself
38  * - option --path eliminated, use --exe instead both for absolute path and file name
39  * - deleted almost every setpriority(), just set it once at startup
40  * - minor enhancements and bugfixes
41  *
42  */
43
44
45 #include <getopt.h>
46 #include <stdio.h>
47 #include <fcntl.h>
48 #include <stdlib.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <unistd.h>
52 #include <sys/types.h>
53 #include <signal.h>
54 #include <sys/resource.h>
55 #include <string.h>
56 #include <dirent.h>
57 #include <errno.h>
58 #include <string.h>
59
60 #include "process.h"
61 #include "procutils.h"
62 #include "list.h"
63
64 //some useful macro
65 #define MIN(a,b) (a<b?a:b)
66 #define MAX(a,b) (a>b?a:b)
67 #define print_caption() printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n")
68 //control time slot in microseconds
69 //each slot is splitted in a working slice and a sleeping slice
70 #define CONTROL_SLOT 100000
71
72 #define MAX_PRIORITY -10
73
74 //the "family"
75 struct process_family pf;
76 //pid of cpulimit
77 int cpulimit_pid;
78 //name of this program (maybe cpulimit...)
79 char *program_name;
80
81 /* CONFIGURATION VARIABLES */
82
83 //verbose mode
84 int verbose = 0;
85 //lazy mode (exits if there is no process)
86 int lazy = 0;
87
88 //how many cpu do we have?
89 int get_cpu_count()
90 {
91         FILE *fd;
92         int cpu_count = 0;
93         char line[100];
94         fd = fopen("/proc/stat", "r");
95         if (fd < 0)
96                 return 0; //are we running Linux??
97         while (fgets(line,sizeof(line),fd)!=NULL) {
98                 if (strncmp(line, "cpu", 3) != 0) break;
99                 cpu_count++;
100         }
101         fclose(fd);
102         return cpu_count - 1;
103 }
104
105 //return t1-t2 in microseconds (no overflow checks, so better watch out!)
106 inline unsigned long timediff(const struct timespec *t1,const struct timespec *t2)
107 {
108         return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_nsec/1000 - t2->tv_nsec/1000);
109 }
110
111 //returns t1-t2 in microseconds
112 inline unsigned long long tv_diff(struct timeval *t1, struct timeval *t2)
113 {
114         return ((unsigned long long)(t1->tv_sec - t2->tv_sec)) * 1000000ULL + t1->tv_usec - t2->tv_usec;
115 }
116
117 //SIGINT and SIGTERM signal handler
118 void quit(int sig)
119 {
120         //let all the processes continue if stopped
121         struct list_node *node = NULL;
122         for (node=pf.members.first; node!= NULL; node=node->next) {
123                 struct process *p = (struct process*)(node->data);
124                 process_close(p->history);
125                 kill(p->pid, SIGCONT);
126         }
127         //free all the memory
128         cleanup_process_family(&pf);
129         exit(0);
130 }
131
132 void print_usage(FILE *stream, int exit_code)
133 {
134         fprintf(stream, "Usage: %s TARGET [OPTIONS...]\n",program_name);
135         fprintf(stream, "   TARGET must be exactly one of these:\n");
136         fprintf(stream, "      -p, --pid=N        pid of the process (implies -z)\n");
137         fprintf(stream, "      -e, --exe=FILE     name of the executable program file or absolute path name\n");
138         fprintf(stream, "   OPTIONS\n");
139         fprintf(stream, "      -l, --limit=N      percentage of cpu allowed from 0 to 100 (required)\n");
140         fprintf(stream, "      -v, --verbose      show control statistics\n");
141         fprintf(stream, "      -z, --lazy         exit if there is no suitable target process, or if it dies\n");
142         fprintf(stream, "      -h, --help         display this help and exit\n");
143         exit(exit_code);
144 }
145
146 void limit_process(int pid, float limit)
147 {
148         //slice of the slot in which the process is allowed to run
149         struct timespec twork;
150         //slice of the slot in which the process is stopped
151         struct timespec tsleep;
152         //when the last twork has started
153         struct timespec startwork;
154         //when the last twork has finished
155         struct timespec endwork;
156         //initialization
157         memset(&twork, 0, sizeof(struct timespec));
158         memset(&tsleep, 0, sizeof(struct timespec));
159         memset(&startwork, 0, sizeof(struct timespec));
160         memset(&endwork, 0, sizeof(struct timespec));   
161         //last working time in microseconds
162         unsigned long workingtime = 0;
163         int i = 0;
164
165         //build the family
166         create_process_family(&pf, pid);
167         struct list_node *node;
168         
169         if (verbose) printf("Members in the family owned by %d: %d\n", pf.father, pf.members.count);
170
171         while(1) {
172
173                 if (i%100==0 && verbose) print_caption();
174
175                 if (i%10==0) {
176                         //update the process family (checks only for new members)
177                         int newborn = check_new_members(&pf);
178                         if (newborn) {
179                                 printf("%d new children processes detected (", newborn);
180                                 int j;
181                                 node = pf.members.last;
182                                 for (j=0; j<newborn; j++) {
183                                         printf("%d", ((struct process*)(node->data))->pid);
184                                         if (j<newborn-1) printf(" ");
185                                         node = node->previous;
186                                 }
187                                 printf(")\n");
188                         }
189                 }
190
191                 //total cpu actual usage (range 0-1)
192                 //1 means that the processes are using 100% cpu
193                 float pcpu = 0;
194                 //rate at which we are keeping active the processes (range 0-1)
195                 //1 means that the process are using all the twork slice
196                 float workingrate = 0;
197
198                 //estimate how much the controlled processes are using the cpu in the working interval
199                 for (node=pf.members.first; node!=NULL; node=node->next) {
200                         struct process *proc = (struct process*)(node->data);
201                         if (process_monitor(proc->history, workingtime, &(proc->history->usage))==-1) {
202                                 //process is dead, remove it from family
203                                 remove_process_from_family(&pf, proc->pid);
204                                 fprintf(stderr,"Process %d dead!\n", proc->pid);
205                         }
206                         pcpu += proc->history->usage.pcpu;
207                         workingrate += proc->history->usage.workingrate;
208                 }
209                 //average value
210                 workingrate /= pf.members.count;
211
212                 //TODO: make workingtime customized for each process, now it's equal for all
213
214                 //adjust work and sleep time slices
215                 if (pcpu>0) {
216                         twork.tv_nsec = MIN(CONTROL_SLOT*limit*1000/pcpu*workingrate,CONTROL_SLOT*1000);
217                 }
218                 else if (pcpu==0) {
219                         twork.tv_nsec = CONTROL_SLOT*1000;
220                 }
221                 else if (pcpu==-1) {
222                         //not yet a valid idea of cpu usage
223                         pcpu = limit;
224                         workingrate = limit;
225                         twork.tv_nsec = MIN(CONTROL_SLOT*limit*1000,CONTROL_SLOT*1000);
226                 }
227                 tsleep.tv_nsec = CONTROL_SLOT*1000-twork.tv_nsec;
228
229                 if (verbose && i%10==0 && i>0) {
230                         printf("%0.2f%%\t%6ld us\t%6ld us\t%0.2f%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100);
231                 }
232
233                 //resume processes
234                 for (node=pf.members.first; node!=NULL; node=node->next) {
235                         struct process *proc = (struct process*)(node->data);
236                         if (kill(proc->pid,SIGCONT)!=0) {
237                                 //process is dead, remove it from family
238                                 fprintf(stderr,"Process %d dead!\n", proc->pid);
239                                 remove_process_from_family(&pf, proc->pid);
240                         }
241                 }
242
243                 //now processes are free to run (same working slice for all)
244                 clock_gettime(CLOCK_REALTIME,&startwork);
245                 nanosleep(&twork,NULL);
246                 clock_gettime(CLOCK_REALTIME,&endwork);
247                 workingtime = timediff(&endwork,&startwork);
248
249                 //stop processes, they have worked enough
250                 //resume processes
251                 for (node=pf.members.first; node!=NULL; node=node->next) {
252                         struct process *proc = (struct process*)(node->data);
253                         if (kill(proc->pid,SIGSTOP)!=0) {
254                                 //process is dead, remove it from family
255                                 fprintf(stderr,"Process %d dead!\n", proc->pid);
256                                 remove_process_from_family(&pf, proc->pid);
257                         }
258                 }
259                 //now the process is forced to sleep
260                 nanosleep(&tsleep,NULL);
261                 i++;
262         }
263         cleanup_process_family(&pf);
264 }
265
266 int main(int argc, char **argv) {
267
268         //get program name
269         char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0]));
270         program_name = p==NULL?argv[0]:(p+1);
271         cpulimit_pid = getpid();
272
273         //argument variables
274         const char *exe = NULL;
275         int perclimit = 0;
276         int pid_ok = 0;
277         int process_ok = 0;
278         int limit_ok = 0;
279         int pid = 0;
280
281         //parse arguments
282         int next_option;
283     int option_index = 0;
284         //A string listing valid short options letters
285         const char* short_options = "p:e:l:vzh";
286         //An array describing valid long options
287         const struct option long_options[] = {
288                 { "pid",        required_argument, NULL,     'p' },
289                 { "exe",        required_argument, NULL,     'e' },
290                 { "limit",      required_argument, NULL,     'l' },
291                 { "verbose",    no_argument,       &verbose, 'v' },
292                 { "lazy",       no_argument,       &lazy,    'z' },
293                 { "help",       no_argument,       NULL,     'h' },
294                 { 0,            0,                 0,         0  }
295         };
296
297         do {
298                 next_option = getopt_long(argc, argv, short_options,long_options, &option_index);
299                 switch(next_option) {
300                         case 'p':
301                                 pid = atoi(optarg);
302                                 //todo: verify pid is valid
303                                 pid_ok = 1;
304                                 process_ok = 1;
305                                 break;
306                         case 'e':
307                                 exe = optarg;
308                                 process_ok = 1;
309                                 break;
310                         case 'l':
311                                 perclimit = atoi(optarg);
312                                 limit_ok = 1;
313                                 break;
314                         case 'v':
315                                 verbose = 1;
316                                 break;
317                         case 'z':
318                                 lazy = 1;
319                                 break;
320                         case 'h':
321                                 print_usage(stdout, 1);
322                                 break;
323                         case '?':
324                                 print_usage(stderr, 1);
325                                 break;
326                         case -1:
327                                 break;
328                         default:
329                                 abort();
330                 }
331         } while(next_option != -1);
332
333         if (pid!=0) {
334                 lazy = 1;
335         }
336         
337         if (!process_ok) {
338                 fprintf(stderr,"Error: You must specify a target process, by name or by PID\n");
339                 print_usage(stderr, 1);
340                 exit(1);
341         }
342         if (pid_ok && exe!=NULL) {
343                 fprintf(stderr, "Error: You must specify exactly one process, by name or by PID\n");
344                 print_usage(stderr, 1);
345                 exit(1);
346         }
347         if (!limit_ok) {
348                 fprintf(stderr,"Error: You must specify a cpu limit percentage\n");
349                 print_usage(stderr, 1);
350                 exit(1);
351         }
352         float limit = perclimit/100.0;
353         int cpu_count = get_cpu_count();
354         if (limit<0 || limit >cpu_count) {
355                 fprintf(stderr,"Error: limit must be in the range 0-%d00\n", cpu_count);
356                 print_usage(stderr, 1);
357                 exit(1);
358         }
359         //parameters are all ok!
360         signal(SIGINT, quit);
361         signal(SIGTERM, quit);
362
363         //try to renice with the best value
364         int old_priority = getpriority(PRIO_PROCESS, 0);
365         int priority = old_priority;
366         while (setpriority(PRIO_PROCESS, 0, priority-1) == 0 && priority>MAX_PRIORITY) {
367                 priority--;     
368         }
369         if (priority != old_priority) {
370                 printf("Priority changed to %d\n", priority);
371         }
372         else {
373                 printf("Cannot change priority\n");
374         }
375         
376         while(1) {
377                 //look for the target process..or wait for it
378                 int ret = 0;
379                 if (pid_ok) {
380                         //search by pid
381                         ret = look_for_process_by_pid(pid);
382                         if (ret == 0) {
383                                 printf("No process found\n");
384                         }
385                         else if (ret < 0) {
386                                 printf("Process found but you aren't allowed to control it\n");
387                         }
388                 }
389                 else {
390                         //search by file or path name
391                         ret = look_for_process_by_name(exe);
392                         if (ret == 0) {
393                                 printf("No process found\n");
394                         }
395                         else if (ret < 0) {
396                                 printf("Process found but you aren't allowed to control it\n");
397                         }
398                         else {
399                                 pid = ret;
400                         }
401                 }
402                 if (ret > 0) {
403                         if (ret == cpulimit_pid) {
404                                 printf("Process %d is cpulimit itself! Aborting to avoid deadlock\n", ret);
405                                 exit(1);
406                         }
407                         printf("Process %d found\n", pid);
408                         //control
409                         limit_process(pid, limit);
410                 }
411                 if (lazy) {
412                         printf("Giving up...\n");
413                         break;
414                 }
415                 sleep(2);
416         }
417         
418         return 0;
419 }
420