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()
25 struct process *next = NULL, *pr = first_process;
37 static struct process *find_process(pid_t pid)
39 struct process *p = first_process;
49 * Create a new process object and insert it into the process list
51 static struct process *new_process(int p)
53 struct process *process;
54 process = (struct process*)malloc(sizeof(struct process));
56 // clean up memory first
57 memset(process, 0, sizeof(struct process));
60 * Do stitching necessary for doubly linked list
63 process->previous = 0;
64 process->next = first_process;
66 process->next->previous = process;
67 first_process = process;
70 process->time_stamp = 0;
71 process->previous_user_time = ULONG_MAX;
72 process->previous_kernel_time = ULONG_MAX;
76 /* process_find_name(process); */
81 /******************************************/
83 /******************************************/
85 static int process_parse_stat(struct process *);
86 static int update_process_table(void);
87 static int calculate_cpu(struct process *);
88 static void process_cleanup(void);
89 static void delete_process(struct process *);
90 /*inline void draw_processes(void);*/
91 static unsigned long calc_cpu_total(void);
92 static void calc_cpu_each(unsigned long);
95 /******************************************/
96 /* Extract information from /proc */
97 /******************************************/
100 * These are the guts that extract information out of /proc.
101 * Anyone hoping to port wmtop should look here first.
103 static int process_parse_stat(struct process *process)
105 struct information *cur;
107 char line[BUFFER_LEN], filename[BUFFER_LEN], procname[BUFFER_LEN];
109 unsigned long user_time = 0;
110 unsigned long kernel_time = 0;
113 char deparenthesised_name[BUFFER_LEN];
117 snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
120 ps = open(filename, O_RDONLY);
123 * The process must have finished in the last few jiffies!
128 * Mark process as up-to-date.
130 process->time_stamp = g_time;
132 rc = read(ps, line, sizeof(line));
138 * Extract cpu times from data in /proc filesystem
141 "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu %*s %*s %*s %d %*s %*s %*s %d %d",
142 procname, &process->user_time, &process->kernel_time,
143 &nice_val, &process->vsize, &process->rss);
147 * Remove parentheses from the process name stored in /proc/ under Linux...
150 /* remove any "kdeinit: " */
151 if (r == strstr(r, "kdeinit")) {
152 snprintf(filename, sizeof(filename),
153 PROCFS_CMDLINE_TEMPLATE, process->pid);
155 ps = open(filename, O_RDONLY);
158 * The process must have finished in the last few jiffies!
162 endl = read(ps, line, sizeof(line));
165 /* null terminate the input */
167 /* account for "kdeinit: " */
168 if ((char *) line == strstr(line, "kdeinit: "))
169 r = ((char *) line) + 9;
173 q = deparenthesised_name;
175 while (*r && *r != ' ')
179 q = deparenthesised_name;
180 while (*r && *r != ')')
188 process->name = strdup(deparenthesised_name);
189 process->rss *= getpagesize();
192 update_total_processes();
196 process->totalmem = (float)(((float) process->rss / cur->memmax) / 10);
197 if (process->previous_user_time == ULONG_MAX)
198 process->previous_user_time = process->user_time;
199 if (process->previous_kernel_time == ULONG_MAX)
200 process->previous_kernel_time = process->kernel_time;
202 /* store the difference of the user_time */
203 user_time = process->user_time - process->previous_user_time;
204 kernel_time = process->kernel_time - process->previous_kernel_time;
206 /* backup the process->user_time for next time around */
207 process->previous_user_time = process->user_time;
208 process->previous_kernel_time = process->kernel_time;
210 /* store only the difference of the user_time here... */
211 process->user_time = user_time;
212 process->kernel_time = kernel_time;
218 /******************************************/
219 /* Update process table */
220 /******************************************/
222 static int update_process_table()
225 struct dirent *entry;
227 if (!(dir = opendir("/proc")))
233 * Get list of processes from /proc directory
235 while ((entry = readdir(dir))) {
240 * Problem reading list of processes
246 if (sscanf(entry->d_name, "%d", &pid) > 0) {
248 p = find_process(pid);
250 p = new_process(pid);
252 /* compute each process cpu usage */
262 /******************************************/
263 /* Get process structure for process pid */
264 /******************************************/
267 * This function seems to hog all of the CPU time. I can't figure out why - it
270 static int calculate_cpu(struct process *process)
274 /* compute each process cpu usage by reading /proc/<proc#>/stat */
275 rc = process_parse_stat(process);
278 /*rc = process_parse_statm(process);
283 * Check name against the exclusion list
285 if (process->counted && exclusion_expression
286 && !regexec(exclusion_expression, process->name, 0, 0, 0))
287 process->counted = 0;
292 /******************************************/
293 /* Strip dead process entries */
294 /******************************************/
296 static void process_cleanup()
299 struct process *p = first_process;
301 struct process *current = p;
303 #if defined(PARANOID)
304 assert(p->id == 0x0badfeed);
305 #endif /* defined(PARANOID) */
309 * Delete processes that have died
311 if (current->time_stamp != g_time)
312 delete_process(current);
316 /******************************************/
317 /* Destroy and remove a process */
318 /******************************************/
320 static void delete_process(struct process *p)
322 #if defined(PARANOID)
323 assert(p->id == 0x0badfeed);
326 * Ensure that deleted processes aren't reused.
329 #endif /* defined(PARANOID) */
332 * Maintain doubly linked list.
335 p->next->previous = p->previous;
337 p->previous->next = p->next;
339 first_process = p->next;
347 /******************************************/
348 /* Calculate cpu total */
349 /******************************************/
351 static unsigned long calc_cpu_total()
353 unsigned long total = 0;
357 char line[BUFFER_LEN];
358 unsigned long cpu = 0;
359 unsigned long nice = 0;
360 unsigned long system = 0;
361 unsigned long idle = 0;
363 ps = open("/proc/stat", O_RDONLY);
364 rc = read(ps, line, sizeof(line));
368 sscanf(line, "%*s %lu %lu %lu %lu", &cpu, &nice, &system, &idle);
369 total = cpu + nice + system + idle;
371 t = total - previous_total;
372 previous_total = total;
378 /******************************************/
379 /* Calculate each processes cpu */
380 /******************************************/
382 inline static void calc_cpu_each(unsigned long total)
384 struct process *p = first_process;
386 /*p->amount = total ?
387 (100.0 * (float) (p->user_time + p->kernel_time) /
390 (100.0 * (p->user_time + p->kernel_time) / total);
392 /* if (p->amount > 100)
398 /******************************************/
399 /* Find the top processes */
400 /******************************************/
402 //static int tot_struct; //for debugging..uncomment this and the 2 printfs in the next two functs
405 * free a sp_process structure
407 void free_sp(struct sorted_process * sp) {
409 // printf("free: %d structs\n",--tot_struct );
413 * create a new sp_process structure
415 struct sorted_process * malloc_sp(struct process * proc) {
416 struct sorted_process * sp;
417 sp = malloc(sizeof(struct sorted_process));
421 // printf("malloc: %d structs\n", ++tot_struct);
426 * cpu comparison function for insert_sp_element
428 int compare_cpu(struct process *a, struct process *b) {
429 if (a->amount < b->amount) return 1;
434 * mem comparison function for insert_sp_element
436 int compare_mem(struct process *a, struct process *b) {
437 if (a->totalmem < b->totalmem) return 1;
442 * insert this process into the list in a sorted fashion,
443 * or destroy it if it doesn't fit on the list
445 int insert_sp_element(
446 struct sorted_process * sp_cur
447 , struct sorted_process ** p_sp_head
448 , struct sorted_process ** p_sp_tail
450 , int (*compare_funct) (struct process *, struct process *)
453 struct sorted_process * sp_readthru=NULL, * sp_destroy=NULL;
454 int did_insert = 0, x = 0;
456 if (*p_sp_head == NULL) {
461 for(sp_readthru=*p_sp_head, x=0; sp_readthru != NULL && x < max_elements; sp_readthru=sp_readthru->less, x++) {
462 if (compare_funct(sp_readthru->proc, sp_cur->proc) && !did_insert) {
463 /* sp_cur is bigger than sp_readthru so insert it before sp_readthru */
464 sp_cur->less=sp_readthru;
465 if (sp_readthru == *p_sp_head) {
466 *p_sp_head = sp_cur; /* insert as the new head of the list */
468 sp_readthru->greater->less = sp_cur; /* insert inside the list */
469 sp_cur->greater = sp_readthru->greater;
471 sp_readthru->greater=sp_cur;
472 did_insert = ++x; /* element was inserted, so increase the counter */
475 if (x < max_elements && sp_readthru == NULL && !did_insert) {
476 /* sp_cur is the smallest element and list isn't full, so insert at the end */
477 (*p_sp_tail)->less=sp_cur;
478 sp_cur->greater=*p_sp_tail;
481 } else if (x >= max_elements) {
482 /* we inserted an element and now the list is too big by one. Destroy the smallest element */
483 sp_destroy = *p_sp_tail;
484 *p_sp_tail = sp_destroy->greater;
485 (*p_sp_tail)->less = NULL;
489 /* sp_cur wasn't added to the sorted list, so destroy it */
496 * copy the procs in the sorted list to the array, and destroy the list
498 void sp_acopy(struct sorted_process *sp_head, struct process ** ar, int max_size)
500 struct sorted_process * sp_cur, * sp_tmp;
503 for (x=0; x < max_size && sp_cur != NULL; x++) {
504 ar[x] = sp_cur->proc;
506 sp_cur= sp_cur->less;
511 // stole from common.c
512 #define NEED(a) ((need_mask & (1 << a)) && ((info.mask & (1 << a)) == 0))
514 /* ****************************************************************** */
515 /* Get a sorted list of the top cpu hogs and top mem hogs. */
516 /* Results are stored in the cpu,mem arrays in decreasing order[0-9]. */
517 /* ****************************************************************** */
519 inline void process_find_top(struct process **cpu, struct process **mem)
521 struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
522 struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
523 struct process *cur_proc = NULL;
524 unsigned long total = 0;
526 if (!top_cpu && !top_mem) return;
528 total = calc_cpu_total(); /* calculate the total of the processor */
529 update_process_table(); /* update the table with process list */
530 calc_cpu_each(total); /* and then the percentage for each task */
531 process_cleanup(); /* cleanup list from exited processes */
533 cur_proc = first_process;
535 while (cur_proc !=NULL) {
536 //printf("\n\n cur_proc: %s %f %f\n",cur_proc->name, cur_proc->totalmem, cur_proc->amount );
538 spc_cur = malloc_sp(cur_proc);
539 insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP, &compare_cpu);
542 spm_cur = malloc_sp(cur_proc);
543 insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP, &compare_mem);
545 cur_proc = cur_proc->next;
547 sp_acopy(spc_head, cpu, MAX_SP);
548 sp_acopy(spm_head, mem, MAX_SP);