2 * Conky, a system monitor, based on torsmo
4 * Any original torsmo code is licensed under the BSD license
6 * All code written since the fork of torsmo is licensed under the GPL
8 * Please see COPYING for details
10 * Copyright (c) 2005 Adi Zaimi, Dan Piponi <dan@tanelorn.demon.co.uk>,
11 * Dave Clark <clarkd@skynet.ca>
12 * Copyright (c) 2005-2007 Brenden Matthews, Philip Kovacs, et. al. (see AUTHORS)
13 * All rights reserved.
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32 static unsigned long g_time = 0;
33 static unsigned long long previous_total = 0;
34 static struct process *first_process = 0;
36 struct process *get_first_process()
43 void free_all_processes()
45 struct process *next = NULL, *pr = first_process;
57 static struct process *find_process(pid_t pid)
59 struct process *p = first_process;
69 * Create a new process object and insert it into the process list
71 static struct process *new_process(int p)
73 struct process *process;
74 process = (struct process*)malloc(sizeof(struct process));
76 // clean up memory first
77 memset(process, 0, sizeof(struct process));
80 * Do stitching necessary for doubly linked list
83 process->previous = 0;
84 process->next = first_process;
86 process->next->previous = process;
87 first_process = process;
90 process->time_stamp = 0;
91 process->previous_user_time = ULONG_MAX;
92 process->previous_kernel_time = ULONG_MAX;
96 /* process_find_name(process); */
101 /******************************************/
103 /******************************************/
105 static int process_parse_stat(struct process *);
106 static int update_process_table(void);
107 static int calculate_cpu(struct process *);
108 static void process_cleanup(void);
109 static void delete_process(struct process *);
110 /*inline void draw_processes(void);*/
111 static unsigned long long calc_cpu_total(void);
112 static void calc_cpu_each(unsigned long long);
115 /******************************************/
116 /* Extract information from /proc */
117 /******************************************/
120 * These are the guts that extract information out of /proc.
121 * Anyone hoping to port wmtop should look here first.
123 static int process_parse_stat(struct process *process)
125 struct information *cur;
127 char line[BUFFER_LEN] = { 0 }, filename[BUFFER_LEN], procname[BUFFER_LEN];
129 unsigned long user_time = 0;
130 unsigned long kernel_time = 0;
133 char deparenthesised_name[BUFFER_LEN];
137 snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
140 ps = open(filename, O_RDONLY);
143 * The process must have finished in the last few jiffies!
148 * Mark process as up-to-date.
150 process->time_stamp = g_time;
152 rc = read(ps, line, sizeof(line));
158 * Extract cpu times from data in /proc filesystem
161 "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu %*s %*s %*s %d %*s %*s %*s %d %d",
162 procname, &process->user_time, &process->kernel_time,
163 &nice_val, &process->vsize, &process->rss);
167 * Remove parentheses from the process name stored in /proc/ under Linux...
170 /* remove any "kdeinit: " */
171 if (r == strstr(r, "kdeinit")) {
172 snprintf(filename, sizeof(filename),
173 PROCFS_CMDLINE_TEMPLATE, process->pid);
175 ps = open(filename, O_RDONLY);
178 * The process must have finished in the last few jiffies!
182 endl = read(ps, line, sizeof(line));
185 /* null terminate the input */
187 /* account for "kdeinit: " */
188 if ((char *) line == strstr(line, "kdeinit: "))
189 r = ((char *) line) + 9;
193 q = deparenthesised_name;
195 while (*r && *r != ' ')
199 q = deparenthesised_name;
200 while (*r && *r != ')')
208 process->name = strdup(deparenthesised_name);
209 process->rss *= getpagesize();
212 update_total_processes();
216 process->totalmem = (float)(((float) process->rss / cur->memmax) / 10);
217 if (process->previous_user_time == ULONG_MAX)
218 process->previous_user_time = process->user_time;
219 if (process->previous_kernel_time == ULONG_MAX)
220 process->previous_kernel_time = process->kernel_time;
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;
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;
230 /* store only the difference of the user_time here... */
231 process->user_time = user_time;
232 process->kernel_time = kernel_time;
238 /******************************************/
239 /* Update process table */
240 /******************************************/
242 static int update_process_table()
245 struct dirent *entry;
247 if (!(dir = opendir("/proc")))
253 * Get list of processes from /proc directory
255 while ((entry = readdir(dir))) {
260 * Problem reading list of processes
266 if (sscanf(entry->d_name, "%d", &pid) > 0) {
268 p = find_process(pid);
270 p = new_process(pid);
272 /* compute each process cpu usage */
282 /******************************************/
283 /* Get process structure for process pid */
284 /******************************************/
287 * This function seems to hog all of the CPU time. I can't figure out why - it
290 static int calculate_cpu(struct process *process)
294 /* compute each process cpu usage by reading /proc/<proc#>/stat */
295 rc = process_parse_stat(process);
298 /*rc = process_parse_statm(process);
303 * Check name against the exclusion list
305 /* if (process->counted && exclusion_expression
306 && !regexec(exclusion_expression, process->name, 0, 0, 0))
307 process->counted = 0;
313 /******************************************/
314 /* Strip dead process entries */
315 /******************************************/
317 static void process_cleanup()
320 struct process *p = first_process;
322 struct process *current = p;
324 #if defined(PARANOID)
325 assert(p->id == 0x0badfeed);
326 #endif /* defined(PARANOID) */
330 * Delete processes that have died
332 if (current->time_stamp != g_time)
333 delete_process(current);
337 /******************************************/
338 /* Destroy and remove a process */
339 /******************************************/
341 static void delete_process(struct process *p)
343 #if defined(PARANOID)
344 assert(p->id == 0x0badfeed);
347 * Ensure that deleted processes aren't reused.
350 #endif /* defined(PARANOID) */
353 * Maintain doubly linked list.
356 p->next->previous = p->previous;
358 p->previous->next = p->next;
360 first_process = p->next;
368 /******************************************/
369 /* Calculate cpu total */
370 /******************************************/
371 #define TMPL_SHORTPROC "%*s %llu %llu %llu %llu"
372 #define TMPL_LONGPROC "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
374 static unsigned long long calc_cpu_total()
376 unsigned long long total = 0;
377 unsigned long long t = 0;
380 char line[BUFFER_LEN] = { 0 };
381 unsigned long long cpu = 0;
382 unsigned long long nice = 0;
383 unsigned long long system = 0;
384 unsigned long long idle = 0;
385 unsigned long long iowait = 0;
386 unsigned long long irq = 0;
387 unsigned long long softirq = 0;
388 unsigned long long steal = 0;
389 char * template = KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGPROC : TMPL_SHORTPROC;
391 ps = open("/proc/stat", O_RDONLY);
392 rc = read(ps, line, sizeof(line));
397 sscanf(line, template, &cpu, &nice, &system, &idle, &iowait, &irq, &softirq, &steal);
398 total = cpu + nice + system + idle + iowait + irq + softirq + steal;
400 t = total - previous_total;
401 previous_total = total;
406 /******************************************/
407 /* Calculate each processes cpu */
408 /******************************************/
410 inline static void calc_cpu_each(unsigned long long total)
412 struct process *p = first_process;
415 100.0 * (p->user_time + p->kernel_time) / (float)total;
421 /******************************************/
422 /* Find the top processes */
423 /******************************************/
426 * free a sp_process structure
428 void free_sp(struct sorted_process * sp) {
433 * create a new sp_process structure
435 struct sorted_process * malloc_sp(struct process * proc) {
436 struct sorted_process * sp;
437 sp = malloc(sizeof(struct sorted_process));
445 * cpu comparison function for insert_sp_element
447 int compare_cpu(struct process *a, struct process *b) {
448 if (a->amount < b->amount) return 1;
453 * mem comparison function for insert_sp_element
455 int compare_mem(struct process *a, struct process *b) {
456 if (a->totalmem < b->totalmem) return 1;
461 * insert this process into the list in a sorted fashion,
462 * or destroy it if it doesn't fit on the list
464 int insert_sp_element(
465 struct sorted_process * sp_cur
466 , struct sorted_process ** p_sp_head
467 , struct sorted_process ** p_sp_tail
469 , int (*compare_funct) (struct process *, struct process *)
472 struct sorted_process * sp_readthru=NULL, * sp_destroy=NULL;
473 int did_insert = 0, x = 0;
475 if (*p_sp_head == NULL) {
480 for(sp_readthru=*p_sp_head, x=0; sp_readthru != NULL && x < max_elements; sp_readthru=sp_readthru->less, x++) {
481 if (compare_funct(sp_readthru->proc, sp_cur->proc) && !did_insert) {
482 /* sp_cur is bigger than sp_readthru so insert it before sp_readthru */
483 sp_cur->less=sp_readthru;
484 if (sp_readthru == *p_sp_head) {
485 *p_sp_head = sp_cur; /* insert as the new head of the list */
487 sp_readthru->greater->less = sp_cur; /* insert inside the list */
488 sp_cur->greater = sp_readthru->greater;
490 sp_readthru->greater=sp_cur;
491 did_insert = ++x; /* element was inserted, so increase the counter */
494 if (x < max_elements && sp_readthru == NULL && !did_insert) {
495 /* sp_cur is the smallest element and list isn't full, so insert at the end */
496 (*p_sp_tail)->less=sp_cur;
497 sp_cur->greater=*p_sp_tail;
500 } else if (x >= max_elements) {
501 /* we inserted an element and now the list is too big by one. Destroy the smallest element */
502 sp_destroy = *p_sp_tail;
503 *p_sp_tail = sp_destroy->greater;
504 (*p_sp_tail)->less = NULL;
508 /* sp_cur wasn't added to the sorted list, so destroy it */
515 * copy the procs in the sorted list to the array, and destroy the list
517 void sp_acopy(struct sorted_process *sp_head, struct process ** ar, int max_size)
519 struct sorted_process * sp_cur, * sp_tmp;
522 for (x=0; x < max_size && sp_cur != NULL; x++) {
523 ar[x] = sp_cur->proc;
525 sp_cur= sp_cur->less;
530 // stole from common.c
531 #define NEED(a) ((need_mask & (1 << a)) && ((info.mask & (1 << a)) == 0))
533 /* ****************************************************************** */
534 /* Get a sorted list of the top cpu hogs and top mem hogs. */
535 /* Results are stored in the cpu,mem arrays in decreasing order[0-9]. */
536 /* ****************************************************************** */
538 inline void process_find_top(struct process **cpu, struct process **mem)
540 struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
541 struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
542 struct process *cur_proc = NULL;
543 unsigned long long total = 0;
545 if (!top_cpu && !top_mem) return;
547 total = calc_cpu_total(); /* calculate the total of the processor */
548 update_process_table(); /* update the table with process list */
549 calc_cpu_each(total); /* and then the percentage for each task */
550 process_cleanup(); /* cleanup list from exited processes */
552 cur_proc = first_process;
554 while (cur_proc !=NULL) {
556 spc_cur = malloc_sp(cur_proc);
557 insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP, &compare_cpu);
560 spm_cur = malloc_sp(cur_proc);
561 insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP, &compare_mem);
563 cur_proc = cur_proc->next;
565 sp_acopy(spc_head, cpu, MAX_SP);
566 sp_acopy(spm_head, mem, MAX_SP);