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()
21 void free_all_processes(struct process *pr)
23 struct process *next = NULL;
34 static struct process *find_process(pid_t pid)
36 struct process *p = first_process;
46 * Create a new process object and insert it into the process list
48 static struct process *new_process(int p)
50 struct process *process;
51 process = (struct process*)malloc(sizeof(struct process));
53 // clean up memory first
54 memset(process, 0, sizeof(struct process));
57 * Do stitching necessary for doubly linked list
60 process->previous = 0;
61 process->next = first_process;
63 process->next->previous = process;
64 first_process = process;
67 process->time_stamp = 0;
68 process->previous_user_time = ULONG_MAX;
69 process->previous_kernel_time = ULONG_MAX;
73 /* process_find_name(process); */
78 /******************************************/
80 /******************************************/
82 static int process_parse_stat(struct process *);
83 static int update_process_table(void);
84 static int calculate_cpu(struct process *);
85 static void process_cleanup(void);
86 static void delete_process(struct process *);
87 /*inline void draw_processes(void);*/
88 static unsigned long calc_cpu_total(void);
89 static void calc_cpu_each(unsigned long);
92 /******************************************/
93 /* Extract information from /proc */
94 /******************************************/
97 * These are the guts that extract information out of /proc.
98 * Anyone hoping to port wmtop should look here first.
100 static int process_parse_stat(struct process *process)
102 struct information *cur;
104 char line[BUFFER_LEN], filename[BUFFER_LEN], procname[BUFFER_LEN];
106 unsigned long user_time = 0;
107 unsigned long kernel_time = 0;
110 char deparenthesised_name[BUFFER_LEN];
114 snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
117 ps = open(filename, O_RDONLY);
120 * The process must have finished in the last few jiffies!
125 * Mark process as up-to-date.
127 process->time_stamp = g_time;
129 rc = read(ps, line, sizeof(line));
135 * Extract cpu times from data in /proc filesystem
138 "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu %*s %*s %*s %d %*s %*s %*s %d %d",
139 procname, &process->user_time, &process->kernel_time,
140 &nice_val, &process->vsize, &process->rss);
144 * Remove parentheses from the process name stored in /proc/ under Linux...
147 /* remove any "kdeinit: " */
148 if (r == strstr(r, "kdeinit")) {
149 snprintf(filename, sizeof(filename),
150 PROCFS_CMDLINE_TEMPLATE, process->pid);
152 ps = open(filename, O_RDONLY);
155 * The process must have finished in the last few jiffies!
159 endl = read(ps, line, sizeof(line));
162 /* null terminate the input */
164 /* account for "kdeinit: " */
165 if ((char *) line == strstr(line, "kdeinit: "))
166 r = ((char *) line) + 9;
170 q = deparenthesised_name;
172 while (*r && *r != ' ')
176 q = deparenthesised_name;
177 while (*r && *r != ')')
185 process->name = strdup(deparenthesised_name);
186 process->rss *= getpagesize();
189 update_total_processes();
193 process->totalmem = (float)(((float) process->rss / cur->memmax) / 10);
194 if (process->previous_user_time == ULONG_MAX)
195 process->previous_user_time = process->user_time;
196 if (process->previous_kernel_time == ULONG_MAX)
197 process->previous_kernel_time = process->kernel_time;
199 /* store the difference of the user_time */
200 user_time = process->user_time - process->previous_user_time;
201 kernel_time = process->kernel_time - process->previous_kernel_time;
203 /* backup the process->user_time for next time around */
204 process->previous_user_time = process->user_time;
205 process->previous_kernel_time = process->kernel_time;
207 /* store only the difference of the user_time here... */
208 process->user_time = user_time;
209 process->kernel_time = kernel_time;
215 /******************************************/
216 /* Update process table */
217 /******************************************/
219 static int update_process_table()
222 struct dirent *entry;
224 if (!(dir = opendir("/proc")))
230 * Get list of processes from /proc directory
232 while ((entry = readdir(dir))) {
237 * Problem reading list of processes
243 if (sscanf(entry->d_name, "%d", &pid) > 0) {
245 p = find_process(pid);
247 p = new_process(pid);
249 /* compute each process cpu usage */
259 /******************************************/
260 /* Get process structure for process pid */
261 /******************************************/
264 * This function seems to hog all of the CPU time. I can't figure out why - it
267 static int calculate_cpu(struct process *process)
271 /* compute each process cpu usage by reading /proc/<proc#>/stat */
272 rc = process_parse_stat(process);
275 /*rc = process_parse_statm(process);
280 * Check name against the exclusion list
282 if (process->counted && exclusion_expression
283 && !regexec(exclusion_expression, process->name, 0, 0, 0))
284 process->counted = 0;
289 /******************************************/
290 /* Strip dead process entries */
291 /******************************************/
293 static void process_cleanup()
296 struct process *p = first_process;
298 struct process *current = p;
300 #if defined(PARANOID)
301 assert(p->id == 0x0badfeed);
302 #endif /* defined(PARANOID) */
306 * Delete processes that have died
308 if (current->time_stamp != g_time)
309 delete_process(current);
313 /******************************************/
314 /* Destroy and remove a process */
315 /******************************************/
317 static void delete_process(struct process *p)
319 #if defined(PARANOID)
320 assert(p->id == 0x0badfeed);
323 * Ensure that deleted processes aren't reused.
326 #endif /* defined(PARANOID) */
329 * Maintain doubly linked list.
332 p->next->previous = p->previous;
334 p->previous->next = p->next;
336 first_process = p->next;
344 /******************************************/
345 /* Calculate cpu total */
346 /******************************************/
348 static unsigned long calc_cpu_total()
350 unsigned long total = 0;
354 char line[BUFFER_LEN];
355 unsigned long cpu = 0;
356 unsigned long nice = 0;
357 unsigned long system = 0;
358 unsigned long idle = 0;
360 ps = open("/proc/stat", O_RDONLY);
361 rc = read(ps, line, sizeof(line));
365 sscanf(line, "%*s %lu %lu %lu %lu", &cpu, &nice, &system, &idle);
366 total = cpu + nice + system + idle;
368 t = total - previous_total;
369 previous_total = total;
375 /******************************************/
376 /* Calculate each processes cpu */
377 /******************************************/
379 inline static void calc_cpu_each(unsigned long total)
381 struct process *p = first_process;
383 /*p->amount = total ?
384 (100.0 * (float) (p->user_time + p->kernel_time) /
387 (100.0 * (p->user_time + p->kernel_time) / total);
389 /* if (p->amount > 100)
395 /******************************************/
396 /* Find the top processes */
397 /******************************************/
401 * cpu comparison function for insert_sp_element
403 int compare_cpu(struct process *a, struct process *b) {
404 if (a->amount < b->amount) return 1;
409 * mem comparison function for insert_sp_element
411 int compare_mem(struct process *a, struct process *b) {
412 if (a->totalmem < b->totalmem) return 1;
417 * insert this process into the list in a sorted fashion,
418 * or destroy it if it doesn't fit on the list
420 int insert_sp_element(
421 struct sorted_process * sp_cur
422 , struct sorted_process ** p_sp_head
423 , struct sorted_process ** p_sp_tail
425 , int (*compare_funct) (struct process *, struct process *)
428 struct sorted_process * sp_readthru=NULL, * sp_destroy=NULL;
429 int did_insert = 0, x = 0;
431 if (*p_sp_head == NULL) {
436 for(sp_readthru=*p_sp_head, x=0; sp_readthru != NULL && x < max_elements; sp_readthru=sp_readthru->less, x++) {
437 if (compare_funct(sp_readthru->proc, sp_cur->proc) && !did_insert) {
438 /* sp_cur is bigger than sp_readthru so insert it before sp_readthru */
439 sp_cur->less=sp_readthru;
440 if (sp_readthru == *p_sp_head) {
441 *p_sp_head = sp_cur; /* insert as the new head of the list */
443 sp_readthru->greater->less = sp_cur; /* insert inside the list */
444 sp_cur->greater = sp_readthru->greater;
446 sp_readthru->greater=sp_cur;
447 did_insert = ++x; /* element was inserted, so increase the counter */
450 if (x < max_elements && sp_readthru == NULL && !did_insert) {
451 /* sp_cur is the smallest element and list isn't full, so insert at the end */
452 (*p_sp_tail)->less=sp_cur;
453 sp_cur->greater=*p_sp_tail;
456 } else if (x == max_elements && sp_readthru != NULL) {
457 /* we inserted an element and now the list is too big by one. Destroy the smallest element */
458 sp_destroy = sp_readthru;
459 sp_readthru->greater->less = NULL;
460 *p_sp_tail = sp_readthru->greater;
464 /* sp_cur wasn't added to the sorted list, so destroy it */
471 * create a new sp_process structure
473 struct sorted_process * malloc_sp(struct process * proc) {
474 struct sorted_process * sp;
475 sp = malloc(sizeof(struct sorted_process));
483 * copy the procs in the sorted list to the array, and destroy the list
485 void sp_acopy(struct sorted_process *sp_head, struct process ** ar, int max_size)
487 struct sorted_process * sp_cur, * sp_tmp;
490 for (x = 0; x < max_size && sp_cur != NULL; x++) {
491 ar[x] = sp_cur->proc;
493 sp_cur = sp_cur->less;
498 // stole from common.c
499 #define NEED(a) ((need_mask & (1 << a)) && ((info.mask & (1 << a)) == 0))
501 /* ****************************************************************** */
502 /* Get a sorted list of the top cpu hogs and top mem hogs. */
503 /* Results are stored in the cpu,mem arrays in decreasing order[0-9]. */
504 /* ****************************************************************** */
506 inline void process_find_top(struct process **cpu, struct process **mem)
508 struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
509 struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
510 struct process *cur_proc = NULL;
511 unsigned long total = 0;
513 if (!top_cpu && !top_mem) return;
515 total = calc_cpu_total(); /* calculate the total of the processor */
516 update_process_table(); /* update the table with process list */
517 calc_cpu_each(total); /* and then the percentage for each task */
518 process_cleanup(); /* cleanup list from exited processes */
520 cur_proc = first_process;
522 while (cur_proc !=NULL) {
524 spc_cur = malloc_sp(cur_proc);
525 insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP, &compare_cpu);
528 spm_cur = malloc_sp(cur_proc);
529 insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP, &compare_mem);
531 cur_proc = cur_proc->next;
533 sp_acopy(spc_head, cpu, MAX_SP);
534 sp_acopy(spm_head, mem, MAX_SP);