add emacs indentation variables to source files in line with current vim settings
[monky] / src / top.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  *
3  * Conky, a system monitor, based on torsmo
4  *
5  * Any original torsmo code is licensed under the BSD license
6  *
7  * All code written since the fork of torsmo is licensed under the GPL
8  *
9  * Please see COPYING for details
10  *
11  * Copyright (c) 2005 Adi Zaimi, Dan Piponi <dan@tanelorn.demon.co.uk>,
12  *                                        Dave Clark <clarkd@skynet.ca>
13  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
14  *      (see AUTHORS)
15  * All rights reserved.
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  *
29  * vim: ts=4 sw=4 noet ai cindent syntax=c
30  *
31  */
32
33 #include "top.h"
34
35 static unsigned long g_time = 0;
36 static unsigned long long previous_total = 0;
37 static struct process *first_process = 0;
38
39 struct process *get_first_process(void)
40 {
41         return first_process;
42 }
43
44 void free_all_processes(void)
45 {
46         struct process *next = NULL, *pr = first_process;
47
48         while (pr) {
49                 next = pr->next;
50                 if (pr->name) {
51                         free(pr->name);
52                 }
53                 free(pr);
54                 pr = next;
55         }
56         first_process = NULL;
57 }
58
59 struct process *get_process_by_name(const char *name)
60 {
61         struct process *p = first_process;
62
63         while (p) {
64                 if (!strcmp(p->name, name))
65                         return p;
66                 p = p->next;
67         }
68         return 0;
69 }
70
71 static struct process *find_process(pid_t pid)
72 {
73         struct process *p = first_process;
74
75         while (p) {
76                 if (p->pid == pid) {
77                         return p;
78                 }
79                 p = p->next;
80         }
81         return 0;
82 }
83
84 /* Create a new process object and insert it into the process list */
85 static struct process *new_process(int p)
86 {
87         struct process *process;
88         process = (struct process *) malloc(sizeof(struct process));
89
90         // clean up memory first
91         memset(process, 0, sizeof(struct process));
92
93         /* Do stitching necessary for doubly linked list */
94         process->name = 0;
95         process->previous = 0;
96         process->next = first_process;
97         if (process->next) {
98                 process->next->previous = process;
99         }
100         first_process = process;
101
102         process->pid = p;
103         process->time_stamp = 0;
104         process->previous_user_time = ULONG_MAX;
105         process->previous_kernel_time = ULONG_MAX;
106 #ifdef IOSTATS
107         process->previous_read_bytes = ULLONG_MAX;
108         process->previous_write_bytes = ULLONG_MAX;
109 #endif
110         process->counted = 1;
111
112         /* process_find_name(process); */
113
114         return process;
115 }
116
117 /******************************************
118  * Functions                                                      *
119  ******************************************/
120
121 /******************************************
122  * Extract information from /proc                 *
123  ******************************************/
124
125 /* These are the guts that extract information out of /proc.
126  * Anyone hoping to port wmtop should look here first. */
127 static int process_parse_stat(struct process *process)
128 {
129         struct information *cur = &info;
130         char line[BUFFER_LEN] = { 0 }, filename[BUFFER_LEN], procname[BUFFER_LEN];
131         int ps;
132         unsigned long user_time = 0;
133         unsigned long kernel_time = 0;
134         int rc;
135         char *r, *q;
136         int endl;
137         int nice_val;
138         char *lparen, *rparen;
139
140         snprintf(filename, sizeof(filename), PROCFS_TEMPLATE, process->pid);
141
142         ps = open(filename, O_RDONLY);
143         if (ps < 0) {
144                 /* The process must have finished in the last few jiffies! */
145                 return 1;
146         }
147
148         /* Mark process as up-to-date. */
149         process->time_stamp = g_time;
150
151         rc = read(ps, line, sizeof(line));
152         close(ps);
153         if (rc < 0) {
154                 return 1;
155         }
156
157         /* Extract cpu times from data in /proc filesystem */
158         lparen = strchr(line, '(');
159         rparen = strrchr(line, ')');
160         if(!lparen || !rparen || rparen < lparen)
161                 return 1; // this should not happen
162
163         rc = MIN((unsigned)(rparen - lparen - 1), sizeof(procname) - 1);
164         strncpy(procname, lparen + 1, rc);
165         procname[rc] = '\0';
166         rc = sscanf(rparen + 1, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu "
167                 "%lu %*s %*s %*s %d %*s %*s %*s %u %u", &process->user_time,
168                 &process->kernel_time, &nice_val, &process->vsize, &process->rss);
169         if (rc < 5) {
170                 return 1;
171         }
172         /* remove any "kdeinit: " */
173         if (procname == strstr(procname, "kdeinit")) {
174                 snprintf(filename, sizeof(filename), PROCFS_CMDLINE_TEMPLATE,
175                         process->pid);
176
177                 ps = open(filename, O_RDONLY);
178                 if (ps < 0) {
179                         /* The process must have finished in the last few jiffies! */
180                         return 1;
181                 }
182
183                 endl = read(ps, line, sizeof(line));
184                 close(ps);
185
186                 /* null terminate the input */
187                 line[endl] = 0;
188                 /* account for "kdeinit: " */
189                 if ((char *) line == strstr(line, "kdeinit: ")) {
190                         r = ((char *) line) + 9;
191                 } else {
192                         r = (char *) line;
193                 }
194
195                 q = procname;
196                 /* stop at space */
197                 while (*r && *r != ' ') {
198                         *q++ = *r++;
199                 }
200                 *q = 0;
201         }
202
203         if (process->name) {
204                 free(process->name);
205         }
206         process->name = strndup(procname, text_buffer_size);
207         process->rss *= getpagesize();
208
209         if (!cur->memmax) {
210                 update_total_processes();
211         }
212
213         process->total_cpu_time = process->user_time + process->kernel_time;
214         process->totalmem = (float) (((float) process->rss / cur->memmax) / 10);
215         if (process->previous_user_time == ULONG_MAX) {
216                 process->previous_user_time = process->user_time;
217         }
218         if (process->previous_kernel_time == ULONG_MAX) {
219                 process->previous_kernel_time = process->kernel_time;
220         }
221
222         /* store the difference of the user_time */
223         user_time = process->user_time - process->previous_user_time;
224         kernel_time = process->kernel_time - process->previous_kernel_time;
225
226         /* backup the process->user_time for next time around */
227         process->previous_user_time = process->user_time;
228         process->previous_kernel_time = process->kernel_time;
229
230         /* store only the difference of the user_time here... */
231         process->user_time = user_time;
232         process->kernel_time = kernel_time;
233
234         return 0;
235 }
236
237 #ifdef IOSTATS
238 static int process_parse_io(struct process *process)
239 {
240         static const char *read_bytes_str="read_bytes:";
241         static const char *write_bytes_str="write_bytes:";
242
243         char line[BUFFER_LEN] = { 0 }, filename[BUFFER_LEN];
244         int ps;
245         int rc;
246         char *pos, *endpos;
247         unsigned long long read_bytes, write_bytes;
248
249         snprintf(filename, sizeof(filename), PROCFS_TEMPLATE_IO, process->pid);
250
251         ps = open(filename, O_RDONLY);
252         if (ps < 0) {
253                 /* The process must have finished in the last few jiffies!
254                  * Or, the kernel doesn't support I/O accounting.
255                  */
256                 return 1;
257         }
258
259         rc = read(ps, line, sizeof(line));
260         close(ps);
261         if (rc < 0) {
262                 return 1;
263         }
264
265         pos = strstr(line, read_bytes_str);
266         if (pos == NULL) {
267                 /* these should not happen (unless the format of the file changes) */
268                 return 1;
269         }
270         pos += strlen(read_bytes_str);
271         process->read_bytes = strtoull(pos, &endpos, 10);
272         if (endpos == pos) {
273                 return 1;
274         }
275
276         pos = strstr(line, write_bytes_str);
277         if (pos == NULL) {
278                 return 1;
279         }
280         pos += strlen(write_bytes_str);
281         process->write_bytes = strtoull(pos, &endpos, 10);
282         if (endpos == pos) {
283                 return 1;
284         }
285
286         if (process->previous_read_bytes == ULLONG_MAX) {
287                 process->previous_read_bytes = process->read_bytes;
288         }
289         if (process->previous_write_bytes == ULLONG_MAX) {
290                 process->previous_write_bytes = process->write_bytes;
291         }
292
293         /* store the difference of the byte counts */
294         read_bytes = process->read_bytes - process->previous_read_bytes;
295         write_bytes = process->write_bytes - process->previous_write_bytes;
296
297         /* backup the counts for next time around */
298         process->previous_read_bytes = process->read_bytes;
299         process->previous_write_bytes = process->write_bytes;
300
301         /* store only the difference here... */
302         process->read_bytes = read_bytes;
303         process->write_bytes = write_bytes;
304
305         return 0;
306 }
307 #endif
308
309 /******************************************
310  * Get process structure for process pid  *
311  ******************************************/
312
313 /* This function seems to hog all of the CPU time.
314  * I can't figure out why - it doesn't do much. */
315 static int calculate_stats(struct process *process)
316 {
317         int rc;
318
319         /* compute each process cpu usage by reading /proc/<proc#>/stat */
320         rc = process_parse_stat(process);
321         if (rc)
322                 return 1;
323         /* rc = process_parse_statm(process); if (rc) return 1; */
324
325 #ifdef IOSTATS
326         rc = process_parse_io(process);
327         if (rc)
328                 return 1;
329 #endif
330
331         /*
332          * Check name against the exclusion list
333          */
334         /* if (process->counted && exclusion_expression &&
335          * !regexec(exclusion_expression, process->name, 0, 0, 0))
336          * process->counted = 0; */
337
338         return 0;
339 }
340
341 /******************************************
342  * Update process table                                   *
343  ******************************************/
344
345 static int update_process_table(void)
346 {
347         DIR *dir;
348         struct dirent *entry;
349
350         if (!(dir = opendir("/proc"))) {
351                 return 1;
352         }
353
354         ++g_time;
355
356         /* Get list of processes from /proc directory */
357         while ((entry = readdir(dir))) {
358                 pid_t pid;
359
360                 if (!entry) {
361                         /* Problem reading list of processes */
362                         closedir(dir);
363                         return 1;
364                 }
365
366                 if (sscanf(entry->d_name, "%d", &pid) > 0) {
367                         struct process *p;
368
369                         p = find_process(pid);
370                         if (!p) {
371                                 p = new_process(pid);
372                         }
373
374                         /* compute each process cpu usage */
375                         calculate_stats(p);
376                 }
377         }
378
379         closedir(dir);
380
381         return 0;
382 }
383
384 /******************************************
385  * Destroy and remove a process           *
386  ******************************************/
387
388 static void delete_process(struct process *p)
389 {
390 #if defined(PARANOID)
391         assert(p->id == 0x0badfeed);
392
393         /*
394          * Ensure that deleted processes aren't reused.
395          */
396         p->id = 0x007babe;
397 #endif /* defined(PARANOID) */
398
399         /*
400          * Maintain doubly linked list.
401          */
402         if (p->next)
403                 p->next->previous = p->previous;
404         if (p->previous)
405                 p->previous->next = p->next;
406         else
407                 first_process = p->next;
408
409         if (p->name) {
410                 free(p->name);
411         }
412         free(p);
413 }
414
415 /******************************************
416  * Strip dead process entries                     *
417  ******************************************/
418
419 static void process_cleanup(void)
420 {
421
422         struct process *p = first_process;
423
424         while (p) {
425                 struct process *current = p;
426
427 #if defined(PARANOID)
428                 assert(p->id == 0x0badfeed);
429 #endif /* defined(PARANOID) */
430
431                 p = p->next;
432                 /* Delete processes that have died */
433                 if (current->time_stamp != g_time) {
434                         delete_process(current);
435                 }
436         }
437 }
438
439 /******************************************
440  * Calculate cpu total                                    *
441  ******************************************/
442 #define TMPL_SHORTPROC "%*s %llu %llu %llu %llu"
443 #define TMPL_LONGPROC "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
444
445 static unsigned long long calc_cpu_total(void)
446 {
447         unsigned long long total = 0;
448         unsigned long long t = 0;
449         int rc;
450         int ps;
451         char line[BUFFER_LEN] = { 0 };
452         unsigned long long cpu = 0;
453         unsigned long long niceval = 0;
454         unsigned long long systemval = 0;
455         unsigned long long idle = 0;
456         unsigned long long iowait = 0;
457         unsigned long long irq = 0;
458         unsigned long long softirq = 0;
459         unsigned long long steal = 0;
460         const char *template =
461                 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGPROC : TMPL_SHORTPROC;
462
463         ps = open("/proc/stat", O_RDONLY);
464         rc = read(ps, line, sizeof(line));
465         close(ps);
466         if (rc < 0) {
467                 return 0;
468         }
469
470         sscanf(line, template, &cpu, &niceval, &systemval, &idle, &iowait, &irq,
471                 &softirq, &steal);
472         total = cpu + niceval + systemval + idle + iowait + irq + softirq + steal;
473
474         t = total - previous_total;
475         previous_total = total;
476
477         return t;
478 }
479
480 /******************************************
481  * Calculate each processes cpu                   *
482  ******************************************/
483
484 inline static void calc_cpu_each(unsigned long long total)
485 {
486         struct process *p = first_process;
487
488         while (p) {
489                 p->amount = 100.0 * (cpu_separate ? info.cpu_count : 1) *
490                         (p->user_time + p->kernel_time) / (float) total;
491
492                 p = p->next;
493         }
494 }
495
496 #ifdef IOSTATS
497 static void calc_io_each(void)
498 {
499         struct process *p;
500         unsigned long long sum = 0;
501         
502         for (p = first_process; p; p = p->next)
503                 sum += p->read_bytes + p->write_bytes;
504
505         if(sum == 0)
506                 sum = 1; /* to avoid having NANs if no I/O occured */
507         for (p = first_process; p; p = p->next)
508                 p->io_perc = 100.0 * (p->read_bytes + p->write_bytes) / (float) sum;
509 }
510 #endif
511
512 /******************************************
513  * Find the top processes                                 *
514  ******************************************/
515
516 /* free a sp_process structure */
517 static void free_sp(struct sorted_process *sp)
518 {
519         free(sp);
520 }
521
522 /* create a new sp_process structure */
523 static struct sorted_process *malloc_sp(struct process *proc)
524 {
525         struct sorted_process *sp;
526         sp = malloc(sizeof(struct sorted_process));
527         sp->greater = NULL;
528         sp->less = NULL;
529         sp->proc = proc;
530         return sp;
531 }
532
533 /* cpu comparison function for insert_sp_element */
534 static int compare_cpu(struct process *a, struct process *b)
535 {
536         if (a->amount < b->amount) {
537                 return 1;
538         } else if (a->amount > b->amount) {
539                 return -1;
540         } else {
541                 return 0;
542         }
543 }
544
545 /* mem comparison function for insert_sp_element */
546 static int compare_mem(struct process *a, struct process *b)
547 {
548         if (a->totalmem < b->totalmem) {
549                 return 1;
550         } else if (a->totalmem > b->totalmem) {
551                 return -1;
552         } else {
553                 return 0;
554         }
555 }
556
557 /* CPU time comparision function for insert_sp_element */
558 static int compare_time(struct process *a, struct process *b)
559 {
560         return b->total_cpu_time - a->total_cpu_time;
561 }
562
563 #ifdef IOSTATS
564 /* I/O comparision function for insert_sp_element */
565 static int compare_io(struct process *a, struct process *b)
566 {
567         if (a->io_perc < b->io_perc) {
568                 return 1;
569         } else if (a->io_perc > b->io_perc) {
570                 return -1;
571         } else {
572                 return 0;
573         }
574 }
575 #endif
576
577 /* insert this process into the list in a sorted fashion,
578  * or destroy it if it doesn't fit on the list */
579 static int insert_sp_element(struct sorted_process *sp_cur,
580                 struct sorted_process **p_sp_head, struct sorted_process **p_sp_tail,
581                 int max_elements, int compare_funct(struct process *, struct process *))
582 {
583
584         struct sorted_process *sp_readthru = NULL, *sp_destroy = NULL;
585         int did_insert = 0, x = 0;
586
587         if (*p_sp_head == NULL) {
588                 *p_sp_head = sp_cur;
589                 *p_sp_tail = sp_cur;
590                 return 1;
591         }
592         for (sp_readthru = *p_sp_head, x = 0;
593                         sp_readthru != NULL && x < max_elements;
594                         sp_readthru = sp_readthru->less, x++) {
595                 if (compare_funct(sp_readthru->proc, sp_cur->proc) > 0 && !did_insert) {
596                         /* sp_cur is bigger than sp_readthru
597                          * so insert it before sp_readthru */
598                         sp_cur->less = sp_readthru;
599                         if (sp_readthru == *p_sp_head) {
600                                 /* insert as the new head of the list */
601                                 *p_sp_head = sp_cur;
602                         } else {
603                                 /* insert inside the list */
604                                 sp_readthru->greater->less = sp_cur;
605                                 sp_cur->greater = sp_readthru->greater;
606                         }
607                         sp_readthru->greater = sp_cur;
608                         /* element was inserted, so increase the counter */
609                         did_insert = ++x;
610                 }
611         }
612         if (x < max_elements && sp_readthru == NULL && !did_insert) {
613                 /* sp_cur is the smallest element and list isn't full,
614                  * so insert at the end */
615                 (*p_sp_tail)->less = sp_cur;
616                 sp_cur->greater = *p_sp_tail;
617                 *p_sp_tail = sp_cur;
618                 did_insert = x;
619         } else if (x >= max_elements) {
620                 /* We inserted an element and now the list is too big by one.
621                  * Destroy the smallest element */
622                 sp_destroy = *p_sp_tail;
623                 *p_sp_tail = sp_destroy->greater;
624                 (*p_sp_tail)->less = NULL;
625                 free_sp(sp_destroy);
626         }
627         if (!did_insert) {
628                 /* sp_cur wasn't added to the sorted list, so destroy it */
629                 free_sp(sp_cur);
630         }
631         return did_insert;
632 }
633
634 /* copy the procs in the sorted list to the array, and destroy the list */
635 static void sp_acopy(struct sorted_process *sp_head, struct process **ar, int max_size)
636 {
637         struct sorted_process *sp_cur, *sp_tmp;
638         int x;
639
640         sp_cur = sp_head;
641         for (x = 0; x < max_size && sp_cur != NULL; x++) {
642                 ar[x] = sp_cur->proc;
643                 sp_tmp = sp_cur;
644                 sp_cur = sp_cur->less;
645                 free_sp(sp_tmp);
646         }
647 }
648
649 /* ****************************************************************** *
650  * Get a sorted list of the top cpu hogs and top mem hogs.                        *
651  * Results are stored in the cpu,mem arrays in decreasing order[0-9]. *
652  * ****************************************************************** */
653
654 void process_find_top(struct process **cpu, struct process **mem,
655                 struct process **ptime
656 #ifdef IOSTATS
657                 , struct process **io
658 #endif
659                 )
660 {
661         struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
662         struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
663         struct sorted_process *spt_head = NULL, *spt_tail = NULL, *spt_cur = NULL;
664 #ifdef IOSTATS
665         struct sorted_process *spi_head = NULL, *spi_tail = NULL, *spi_cur = NULL;
666 #endif
667         struct process *cur_proc = NULL;
668         unsigned long long total = 0;
669
670         if (!top_cpu && !top_mem && !top_time
671 #ifdef IOSTATS
672                         && !top_io
673 #endif
674            ) {
675                 return;
676         }
677
678         total = calc_cpu_total();       /* calculate the total of the processor */
679         update_process_table();         /* update the table with process list */
680         calc_cpu_each(total);           /* and then the percentage for each task */
681         process_cleanup();                      /* cleanup list from exited processes */
682 #ifdef IOSTATS
683         calc_io_each();                 /* percentage of I/O for each task */
684 #endif
685
686         cur_proc = first_process;
687
688         while (cur_proc != NULL) {
689                 if (top_cpu) {
690                         spc_cur = malloc_sp(cur_proc);
691                         insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP,
692                                 &compare_cpu);
693                 }
694                 if (top_mem) {
695                         spm_cur = malloc_sp(cur_proc);
696                         insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP,
697                                 &compare_mem);
698                 }
699                 if (top_time) {
700                         spt_cur = malloc_sp(cur_proc);
701                         insert_sp_element(spt_cur, &spt_head, &spt_tail, MAX_SP,
702                                 &compare_time);
703                 }
704 #ifdef IOSTATS
705                 if (top_io) {
706                         spi_cur = malloc_sp(cur_proc);
707                         insert_sp_element(spi_cur, &spi_head, &spi_tail, MAX_SP,
708                                 &compare_io);
709                 }
710 #endif
711                 cur_proc = cur_proc->next;
712         }
713
714         if (top_cpu)
715                 sp_acopy(spc_head, cpu, MAX_SP);
716         if (top_mem)
717                 sp_acopy(spm_head, mem, MAX_SP);
718         if (top_time)
719                 sp_acopy(spt_head, ptime, MAX_SP);
720 #ifdef IOSTATS
721         if (top_io)
722                 sp_acopy(spi_head, io,MAX_SP);
723 #endif
724 }