2 * Conky, a system monitor, based on torsmo
4 * This program is licensed under BSD license, read COPYING
11 static regex_t *exclusion_expression = 0;
12 static unsigned long g_time = 0;
13 static unsigned long previous_total = 0;
14 static struct process *first_process = 0;
16 struct process *get_first_process()
23 void free_all_processes(struct process *pr)
25 struct process *next = NULL;
36 static struct process *find_process(pid_t pid)
38 struct process *p = first_process;
48 * Create a new process object and insert it into the process list
50 static struct process *new_process(int p)
52 struct process *process;
53 process = (struct process*)malloc(sizeof(struct process));
55 // clean up memory first
56 memset(process, 0, sizeof(struct process));
59 * Do stitching necessary for doubly linked list
62 process->previous = 0;
63 process->next = first_process;
65 process->next->previous = process;
66 first_process = process;
69 process->time_stamp = 0;
70 process->previous_user_time = ULONG_MAX;
71 process->previous_kernel_time = ULONG_MAX;
75 /* process_find_name(process); */
80 /******************************************/
82 /******************************************/
84 static int process_parse_stat(struct process *);
85 static int update_process_table(void);
86 static int calculate_cpu(struct process *);
87 static void process_cleanup(void);
88 static void delete_process(struct process *);
89 /*inline void draw_processes(void);*/
90 static unsigned long calc_cpu_total(void);
91 static void calc_cpu_each(unsigned long);
94 /******************************************/
95 /* Extract information from /proc */
96 /******************************************/
99 * These are the guts that extract information out of /proc.
100 * Anyone hoping to port wmtop should look here first.
102 static int process_parse_stat(struct process *process)
104 struct information *cur;
106 char line[BUFFER_LEN], filename[BUFFER_LEN], procname[BUFFER_LEN];
108 unsigned long user_time = 0;
109 unsigned long kernel_time = 0;
112 char deparenthesised_name[BUFFER_LEN];
116 snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
119 ps = open(filename, O_RDONLY);
122 * The process must have finished in the last few jiffies!
127 * Mark process as up-to-date.
129 process->time_stamp = g_time;
131 rc = read(ps, line, sizeof(line));
137 * Extract cpu times from data in /proc filesystem
140 "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu %*s %*s %*s %d %*s %*s %*s %d %d",
141 procname, &process->user_time, &process->kernel_time,
142 &nice_val, &process->vsize, &process->rss);
146 * Remove parentheses from the process name stored in /proc/ under Linux...
149 /* remove any "kdeinit: " */
150 if (r == strstr(r, "kdeinit")) {
151 snprintf(filename, sizeof(filename),
152 PROCFS_CMDLINE_TEMPLATE, process->pid);
154 ps = open(filename, O_RDONLY);
157 * The process must have finished in the last few jiffies!
161 endl = read(ps, line, sizeof(line));
164 /* null terminate the input */
166 /* account for "kdeinit: " */
167 if ((char *) line == strstr(line, "kdeinit: "))
168 r = ((char *) line) + 9;
172 q = deparenthesised_name;
174 while (*r && *r != ' ')
178 q = deparenthesised_name;
179 while (*r && *r != ')')
187 process->name = strdup(deparenthesised_name);
188 process->rss *= getpagesize();
191 update_total_processes();
195 process->totalmem = (float)(((float) process->rss / cur->memmax) / 10);
196 if (process->previous_user_time == ULONG_MAX)
197 process->previous_user_time = process->user_time;
198 if (process->previous_kernel_time == ULONG_MAX)
199 process->previous_kernel_time = process->kernel_time;
201 /* store the difference of the user_time */
202 user_time = process->user_time - process->previous_user_time;
203 kernel_time = process->kernel_time - process->previous_kernel_time;
205 /* backup the process->user_time for next time around */
206 process->previous_user_time = process->user_time;
207 process->previous_kernel_time = process->kernel_time;
209 /* store only the difference of the user_time here... */
210 process->user_time = user_time;
211 process->kernel_time = kernel_time;
217 /******************************************/
218 /* Update process table */
219 /******************************************/
221 static int update_process_table()
224 struct dirent *entry;
226 if (!(dir = opendir("/proc")))
232 * Get list of processes from /proc directory
234 while ((entry = readdir(dir))) {
239 * Problem reading list of processes
245 if (sscanf(entry->d_name, "%d", &pid) > 0) {
247 p = find_process(pid);
249 p = new_process(pid);
251 /* compute each process cpu usage */
261 /******************************************/
262 /* Get process structure for process pid */
263 /******************************************/
266 * This function seems to hog all of the CPU time. I can't figure out why - it
269 static int calculate_cpu(struct process *process)
273 /* compute each process cpu usage by reading /proc/<proc#>/stat */
274 rc = process_parse_stat(process);
277 /*rc = process_parse_statm(process);
282 * Check name against the exclusion list
284 if (process->counted && exclusion_expression
285 && !regexec(exclusion_expression, process->name, 0, 0, 0))
286 process->counted = 0;
291 /******************************************/
292 /* Strip dead process entries */
293 /******************************************/
295 static void process_cleanup()
298 struct process *p = first_process;
300 struct process *current = p;
302 #if defined(PARANOID)
303 assert(p->id == 0x0badfeed);
304 #endif /* defined(PARANOID) */
308 * Delete processes that have died
310 if (current->time_stamp != g_time)
311 delete_process(current);
315 /******************************************/
316 /* Destroy and remove a process */
317 /******************************************/
319 static void delete_process(struct process *p)
321 #if defined(PARANOID)
322 assert(p->id == 0x0badfeed);
325 * Ensure that deleted processes aren't reused.
328 #endif /* defined(PARANOID) */
331 * Maintain doubly linked list.
334 p->next->previous = p->previous;
336 p->previous->next = p->next;
338 first_process = p->next;
346 /******************************************/
347 /* Calculate cpu total */
348 /******************************************/
350 static unsigned long calc_cpu_total()
352 unsigned long total = 0;
356 char line[BUFFER_LEN];
357 unsigned long cpu = 0;
358 unsigned long nice = 0;
359 unsigned long system = 0;
360 unsigned long idle = 0;
362 ps = open("/proc/stat", O_RDONLY);
363 rc = read(ps, line, sizeof(line));
367 sscanf(line, "%*s %lu %lu %lu %lu", &cpu, &nice, &system, &idle);
368 total = cpu + nice + system + idle;
370 t = total - previous_total;
371 previous_total = total;
377 /******************************************/
378 /* Calculate each processes cpu */
379 /******************************************/
381 inline static void calc_cpu_each(unsigned long total)
383 struct process *p = first_process;
385 /*p->amount = total ?
386 (100.0 * (float) (p->user_time + p->kernel_time) /
389 (100.0 * (p->user_time + p->kernel_time) / total);
391 /* if (p->amount > 100)
397 /******************************************/
398 /* Find the top processes */
399 /******************************************/
401 // static int tot_struct; //for debugging..uncomment this and the 2 printfs in the next two functs
404 * free a sp_process structure
406 void free_sp(struct sorted_process * sp) {
408 //printf("free: %d structs\n",--tot_struct );
412 * create a new sp_process structure
414 struct sorted_process * malloc_sp(struct process * proc) {
415 struct sorted_process * sp;
416 sp = malloc(sizeof(struct sorted_process));
420 //printf("malloc: %d structs\n", ++tot_struct);
425 * cpu comparison function for insert_sp_element
427 int compare_cpu(struct process *a, struct process *b) {
428 if (a->amount < b->amount) return 1;
433 * mem comparison function for insert_sp_element
435 int compare_mem(struct process *a, struct process *b) {
436 if (a->totalmem < b->totalmem) return 1;
441 * insert this process into the list in a sorted fashion,
442 * or destroy it if it doesn't fit on the list
444 int insert_sp_element(
445 struct sorted_process * sp_cur
446 , struct sorted_process ** p_sp_head
447 , struct sorted_process ** p_sp_tail
449 , int (*compare_funct) (struct process *, struct process *)
452 struct sorted_process * sp_readthru=NULL, * sp_destroy=NULL;
453 int did_insert = 0, x = 0;
455 if (*p_sp_head == NULL) {
460 for(sp_readthru=*p_sp_head, x=0; sp_readthru != NULL && x < max_elements; sp_readthru=sp_readthru->less, x++) {
461 if (compare_funct(sp_readthru->proc, sp_cur->proc) && !did_insert) {
462 /* sp_cur is bigger than sp_readthru so insert it before sp_readthru */
463 sp_cur->less=sp_readthru;
464 if (sp_readthru == *p_sp_head) {
465 *p_sp_head = sp_cur; /* insert as the new head of the list */
467 sp_readthru->greater->less = sp_cur; /* insert inside the list */
468 sp_cur->greater = sp_readthru->greater;
470 sp_readthru->greater=sp_cur;
471 did_insert = ++x; /* element was inserted, so increase the counter */
474 if (x < max_elements && sp_readthru == NULL && !did_insert) {
475 /* sp_cur is the smallest element and list isn't full, so insert at the end */
476 (*p_sp_tail)->less=sp_cur;
477 sp_cur->greater=*p_sp_tail;
480 } else if (x >= max_elements) {
481 /* we inserted an element and now the list is too big by one. Destroy the smallest element */
482 sp_destroy = *p_sp_tail;
483 *p_sp_tail = sp_destroy->greater;
484 (*p_sp_tail)->less = NULL;
488 /* sp_cur wasn't added to the sorted list, so destroy it */
495 * copy the procs in the sorted list to the array, and destroy the list
497 void sp_acopy(struct sorted_process *sp_head, struct process ** ar, int max_size)
499 struct sorted_process * sp_cur, * sp_tmp;
502 for (x=0; x < max_size && sp_cur != NULL; x++) {
503 ar[x] = sp_cur->proc;
505 sp_cur= sp_cur->less;
510 // stole from common.c
511 #define NEED(a) ((need_mask & (1 << a)) && ((info.mask & (1 << a)) == 0))
513 /* ****************************************************************** */
514 /* Get a sorted list of the top cpu hogs and top mem hogs. */
515 /* Results are stored in the cpu,mem arrays in decreasing order[0-9]. */
516 /* ****************************************************************** */
518 inline void process_find_top(struct process **cpu, struct process **mem)
520 struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
521 struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
522 struct process *cur_proc = NULL;
523 unsigned long total = 0;
525 if (!top_cpu && !top_mem) return;
527 total = calc_cpu_total(); /* calculate the total of the processor */
528 update_process_table(); /* update the table with process list */
529 calc_cpu_each(total); /* and then the percentage for each task */
530 process_cleanup(); /* cleanup list from exited processes */
532 cur_proc = first_process;
534 while (cur_proc !=NULL) {
535 //printf("\n\n cur_proc: %s %f %f\n",cur_proc->name, cur_proc->totalmem, cur_proc->amount );
537 spc_cur = malloc_sp(cur_proc);
538 insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP, &compare_cpu);
541 spm_cur = malloc_sp(cur_proc);
542 insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP, &compare_mem);
544 cur_proc = cur_proc->next;
546 sp_acopy(spc_head, cpu, MAX_SP);
547 sp_acopy(spm_head, mem, MAX_SP);