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->total_cpu_time = process->user_time + process->kernel_time;
217 process->totalmem = (float)(((float) process->rss / cur->memmax) / 10);
218 if (process->previous_user_time == ULONG_MAX)
219 process->previous_user_time = process->user_time;
220 if (process->previous_kernel_time == ULONG_MAX)
221 process->previous_kernel_time = process->kernel_time;
223 /* store the difference of the user_time */
224 user_time = process->user_time - process->previous_user_time;
225 kernel_time = process->kernel_time - process->previous_kernel_time;
227 /* backup the process->user_time for next time around */
228 process->previous_user_time = process->user_time;
229 process->previous_kernel_time = process->kernel_time;
231 /* store only the difference of the user_time here... */
232 process->user_time = user_time;
233 process->kernel_time = kernel_time;
239 /******************************************/
240 /* Update process table */
241 /******************************************/
243 static int update_process_table()
246 struct dirent *entry;
248 if (!(dir = opendir("/proc")))
254 * Get list of processes from /proc directory
256 while ((entry = readdir(dir))) {
261 * Problem reading list of processes
267 if (sscanf(entry->d_name, "%d", &pid) > 0) {
269 p = find_process(pid);
271 p = new_process(pid);
273 /* compute each process cpu usage */
283 /******************************************/
284 /* Get process structure for process pid */
285 /******************************************/
288 * This function seems to hog all of the CPU time. I can't figure out why - it
291 static int calculate_cpu(struct process *process)
295 /* compute each process cpu usage by reading /proc/<proc#>/stat */
296 rc = process_parse_stat(process);
299 /*rc = process_parse_statm(process);
304 * Check name against the exclusion list
306 /* if (process->counted && exclusion_expression
307 && !regexec(exclusion_expression, process->name, 0, 0, 0))
308 process->counted = 0;
314 /******************************************/
315 /* Strip dead process entries */
316 /******************************************/
318 static void process_cleanup()
321 struct process *p = first_process;
323 struct process *current = p;
325 #if defined(PARANOID)
326 assert(p->id == 0x0badfeed);
327 #endif /* defined(PARANOID) */
331 * Delete processes that have died
333 if (current->time_stamp != g_time)
334 delete_process(current);
338 /******************************************/
339 /* Destroy and remove a process */
340 /******************************************/
342 static void delete_process(struct process *p)
344 #if defined(PARANOID)
345 assert(p->id == 0x0badfeed);
348 * Ensure that deleted processes aren't reused.
351 #endif /* defined(PARANOID) */
354 * Maintain doubly linked list.
357 p->next->previous = p->previous;
359 p->previous->next = p->next;
361 first_process = p->next;
369 /******************************************/
370 /* Calculate cpu total */
371 /******************************************/
372 #define TMPL_SHORTPROC "%*s %llu %llu %llu %llu"
373 #define TMPL_LONGPROC "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
375 static unsigned long long calc_cpu_total()
377 unsigned long long total = 0;
378 unsigned long long t = 0;
381 char line[BUFFER_LEN] = { 0 };
382 unsigned long long cpu = 0;
383 unsigned long long nice = 0;
384 unsigned long long system = 0;
385 unsigned long long idle = 0;
386 unsigned long long iowait = 0;
387 unsigned long long irq = 0;
388 unsigned long long softirq = 0;
389 unsigned long long steal = 0;
390 char * template = KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGPROC : TMPL_SHORTPROC;
392 ps = open("/proc/stat", O_RDONLY);
393 rc = read(ps, line, sizeof(line));
398 sscanf(line, template, &cpu, &nice, &system, &idle, &iowait, &irq, &softirq, &steal);
399 total = cpu + nice + system + idle + iowait + irq + softirq + steal;
401 t = total - previous_total;
402 previous_total = total;
407 /******************************************/
408 /* Calculate each processes cpu */
409 /******************************************/
411 inline static void calc_cpu_each(unsigned long long total)
413 struct process *p = first_process;
416 100.0 * (cpu_separate ? info.cpu_count : 1) * (p->user_time + p->kernel_time) / (float)total;
422 /******************************************/
423 /* Find the top processes */
424 /******************************************/
427 * free a sp_process structure
429 void free_sp(struct sorted_process * sp) {
434 * create a new sp_process structure
436 struct sorted_process * malloc_sp(struct process * proc) {
437 struct sorted_process * sp;
438 sp = malloc(sizeof(struct sorted_process));
446 * cpu comparison function for insert_sp_element
448 int compare_cpu(struct process *a, struct process *b) {
449 if (a->amount < b->amount) return 1;
454 * mem comparison function for insert_sp_element
456 int compare_mem(struct process *a, struct process *b) {
457 if (a->totalmem < b->totalmem) return 1;
462 * insert this process into the list in a sorted fashion,
463 * or destroy it if it doesn't fit on the list
465 int insert_sp_element(
466 struct sorted_process * sp_cur
467 , struct sorted_process ** p_sp_head
468 , struct sorted_process ** p_sp_tail
470 , int (*compare_funct) (struct process *, struct process *)
473 struct sorted_process * sp_readthru=NULL, * sp_destroy=NULL;
474 int did_insert = 0, x = 0;
476 if (*p_sp_head == NULL) {
481 for(sp_readthru=*p_sp_head, x=0; sp_readthru != NULL && x < max_elements; sp_readthru=sp_readthru->less, x++) {
482 if (compare_funct(sp_readthru->proc, sp_cur->proc) && !did_insert) {
483 /* sp_cur is bigger than sp_readthru so insert it before sp_readthru */
484 sp_cur->less=sp_readthru;
485 if (sp_readthru == *p_sp_head) {
486 *p_sp_head = sp_cur; /* insert as the new head of the list */
488 sp_readthru->greater->less = sp_cur; /* insert inside the list */
489 sp_cur->greater = sp_readthru->greater;
491 sp_readthru->greater=sp_cur;
492 did_insert = ++x; /* element was inserted, so increase the counter */
495 if (x < max_elements && sp_readthru == NULL && !did_insert) {
496 /* sp_cur is the smallest element and list isn't full, so insert at the end */
497 (*p_sp_tail)->less=sp_cur;
498 sp_cur->greater=*p_sp_tail;
501 } else if (x >= max_elements) {
502 /* we inserted an element and now the list is too big by one. Destroy the smallest element */
503 sp_destroy = *p_sp_tail;
504 *p_sp_tail = sp_destroy->greater;
505 (*p_sp_tail)->less = NULL;
509 /* sp_cur wasn't added to the sorted list, so destroy it */
516 * copy the procs in the sorted list to the array, and destroy the list
518 void sp_acopy(struct sorted_process *sp_head, struct process ** ar, int max_size)
520 struct sorted_process * sp_cur, * sp_tmp;
523 for (x=0; x < max_size && sp_cur != NULL; x++) {
524 ar[x] = sp_cur->proc;
526 sp_cur= sp_cur->less;
531 // stole from common.c
532 #define NEED(a) ((need_mask & (1 << a)) && ((info.mask & (1 << a)) == 0))
534 /* ****************************************************************** */
535 /* Get a sorted list of the top cpu hogs and top mem hogs. */
536 /* Results are stored in the cpu,mem arrays in decreasing order[0-9]. */
537 /* ****************************************************************** */
539 inline void process_find_top(struct process **cpu, struct process **mem)
541 struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
542 struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
543 struct process *cur_proc = NULL;
544 unsigned long long total = 0;
546 if (!top_cpu && !top_mem) return;
548 total = calc_cpu_total(); /* calculate the total of the processor */
549 update_process_table(); /* update the table with process list */
550 calc_cpu_each(total); /* and then the percentage for each task */
551 process_cleanup(); /* cleanup list from exited processes */
553 cur_proc = first_process;
555 while (cur_proc !=NULL) {
557 spc_cur = malloc_sp(cur_proc);
558 insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP, &compare_cpu);
561 spm_cur = malloc_sp(cur_proc);
562 insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP, &compare_mem);
564 cur_proc = cur_proc->next;
566 sp_acopy(spc_head, cpu, MAX_SP);
567 sp_acopy(spm_head, mem, MAX_SP);