2 * Conky, a system monitor, based on torsmo
4 * This program is licensed under BSD license, read COPYING
11 static unsigned long g_time = 0;
12 static unsigned long long previous_total = 0;
13 static struct process *first_process = 0;
15 struct process *get_first_process()
22 void free_all_processes()
24 struct process *next = NULL, *pr = first_process;
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 long calc_cpu_total(void);
91 static void calc_cpu_each(unsigned long 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;
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 /******************************************/
350 #define TMPL_SHORTPROC "%*s %llu %llu %llu %llu"
351 #define TMPL_LONGPROC "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
353 static unsigned long long calc_cpu_total()
355 unsigned long long total = 0;
356 unsigned long long t = 0;
359 char line[BUFFER_LEN];
360 unsigned long long cpu = 0;
361 unsigned long long nice = 0;
362 unsigned long long system = 0;
363 unsigned long long idle = 0;
364 unsigned long long iowait = 0;
365 unsigned long long irq = 0;
366 unsigned long long softirq = 0;
367 unsigned long long steal = 0;
368 char * template = KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGPROC : TMPL_SHORTPROC;
370 ps = open("/proc/stat", O_RDONLY);
371 rc = read(ps, line, sizeof(line));
376 sscanf(line, template, &cpu, &nice, &system, &idle, &iowait, &irq, &softirq, &steal);
377 total = cpu + nice + system + idle + iowait + irq + softirq + steal;
379 t = total - previous_total;
380 previous_total = total;
385 /******************************************/
386 /* Calculate each processes cpu */
387 /******************************************/
389 inline static void calc_cpu_each(unsigned long long total)
391 struct process *p = first_process;
394 100.0 * (p->user_time + p->kernel_time) / (float)total;
400 /******************************************/
401 /* Find the top processes */
402 /******************************************/
405 * free a sp_process structure
407 void free_sp(struct sorted_process * sp) {
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));
424 * cpu comparison function for insert_sp_element
426 int compare_cpu(struct process *a, struct process *b) {
427 if (a->amount < b->amount) return 1;
432 * mem comparison function for insert_sp_element
434 int compare_mem(struct process *a, struct process *b) {
435 if (a->totalmem < b->totalmem) return 1;
440 * insert this process into the list in a sorted fashion,
441 * or destroy it if it doesn't fit on the list
443 int insert_sp_element(
444 struct sorted_process * sp_cur
445 , struct sorted_process ** p_sp_head
446 , struct sorted_process ** p_sp_tail
448 , int (*compare_funct) (struct process *, struct process *)
451 struct sorted_process * sp_readthru=NULL, * sp_destroy=NULL;
452 int did_insert = 0, x = 0;
454 if (*p_sp_head == NULL) {
459 for(sp_readthru=*p_sp_head, x=0; sp_readthru != NULL && x < max_elements; sp_readthru=sp_readthru->less, x++) {
460 if (compare_funct(sp_readthru->proc, sp_cur->proc) && !did_insert) {
461 /* sp_cur is bigger than sp_readthru so insert it before sp_readthru */
462 sp_cur->less=sp_readthru;
463 if (sp_readthru == *p_sp_head) {
464 *p_sp_head = sp_cur; /* insert as the new head of the list */
466 sp_readthru->greater->less = sp_cur; /* insert inside the list */
467 sp_cur->greater = sp_readthru->greater;
469 sp_readthru->greater=sp_cur;
470 did_insert = ++x; /* element was inserted, so increase the counter */
473 if (x < max_elements && sp_readthru == NULL && !did_insert) {
474 /* sp_cur is the smallest element and list isn't full, so insert at the end */
475 (*p_sp_tail)->less=sp_cur;
476 sp_cur->greater=*p_sp_tail;
479 } else if (x >= max_elements) {
480 /* we inserted an element and now the list is too big by one. Destroy the smallest element */
481 sp_destroy = *p_sp_tail;
482 *p_sp_tail = sp_destroy->greater;
483 (*p_sp_tail)->less = NULL;
487 /* sp_cur wasn't added to the sorted list, so destroy it */
494 * copy the procs in the sorted list to the array, and destroy the list
496 void sp_acopy(struct sorted_process *sp_head, struct process ** ar, int max_size)
498 struct sorted_process * sp_cur, * sp_tmp;
501 for (x=0; x < max_size && sp_cur != NULL; x++) {
502 ar[x] = sp_cur->proc;
504 sp_cur= sp_cur->less;
509 // stole from common.c
510 #define NEED(a) ((need_mask & (1 << a)) && ((info.mask & (1 << a)) == 0))
512 /* ****************************************************************** */
513 /* Get a sorted list of the top cpu hogs and top mem hogs. */
514 /* Results are stored in the cpu,mem arrays in decreasing order[0-9]. */
515 /* ****************************************************************** */
517 inline void process_find_top(struct process **cpu, struct process **mem)
519 struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
520 struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
521 struct process *cur_proc = NULL;
522 unsigned long long total = 0;
524 if (!top_cpu && !top_mem) return;
526 total = calc_cpu_total(); /* calculate the total of the processor */
527 update_process_table(); /* update the table with process list */
528 calc_cpu_each(total); /* and then the percentage for each task */
529 process_cleanup(); /* cleanup list from exited processes */
531 cur_proc = first_process;
533 while (cur_proc !=NULL) {
535 spc_cur = malloc_sp(cur_proc);
536 insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP, &compare_cpu);
539 spm_cur = malloc_sp(cur_proc);
540 insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP, &compare_mem);
542 cur_proc = cur_proc->next;
544 sp_acopy(spc_head, cpu, MAX_SP);
545 sp_acopy(spm_head, mem, MAX_SP);