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 int g_time = 0;
13 static unsigned long previous_total = 0;
14 static struct process *first_process = 0;
18 static struct process *find_process(pid_t pid)
20 struct process *p = first_process;
30 * Create a new process object and insert it into the process list
32 static struct process *new_process(int p)
34 struct process *process;
35 process = (struct process*)malloc(sizeof(struct process));
37 // clean up memory first
38 memset(process, 0, sizeof(struct process));
41 * Do stitching necessary for doubly linked list
44 process->previous = 0;
45 process->next = first_process;
47 process->next->previous = process;
48 first_process = process;
51 process->time_stamp = 0;
52 process->previous_user_time = INT_MAX;
53 process->previous_kernel_time = INT_MAX;
57 /* process_find_name(process); */
62 /******************************************/
64 /******************************************/
66 static int process_parse_stat(struct process *);
67 static int update_process_table(void);
68 static int calculate_cpu(struct process *);
69 static void process_cleanup(void);
70 static void delete_process(struct process *);
71 /*inline void draw_processes(void);*/
72 static unsigned long calc_cpu_total(void);
73 static void calc_cpu_each(unsigned long);
76 /******************************************/
77 /* Extract information from /proc */
78 /******************************************/
81 * These are the guts that extract information out of /proc.
82 * Anyone hoping to port wmtop should look here first.
84 static int process_parse_stat(struct process *process)
86 struct information *cur;
88 char line[BUFFER_LEN], filename[BUFFER_LEN], procname[BUFFER_LEN];
90 unsigned long user_time = 0;
91 unsigned long kernel_time = 0;
94 char deparenthesised_name[BUFFER_LEN];
98 snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
101 ps = open(filename, O_RDONLY);
104 * The process must have finished in the last few jiffies!
109 * Mark process as up-to-date.
111 process->time_stamp = g_time;
113 rc = read(ps, line, sizeof(line));
119 * Extract cpu times from data in /proc filesystem
122 "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu %*s %*s %*s %d %*s %*s %*s %d %d",
123 procname, &process->user_time, &process->kernel_time,
124 &nice_val, &process->vsize, &process->rss);
128 * Remove parentheses from the process name stored in /proc/ under Linux...
131 /* remove any "kdeinit: " */
132 if (r == strstr(r, "kdeinit")) {
133 snprintf(filename, sizeof(filename),
134 PROCFS_CMDLINE_TEMPLATE, process->pid);
136 ps = open(filename, O_RDONLY);
139 * The process must have finished in the last few jiffies!
143 endl = read(ps, line, sizeof(line));
146 /* null terminate the input */
148 /* account for "kdeinit: " */
149 if ((char *) line == strstr(line, "kdeinit: "))
150 r = ((char *) line) + 9;
154 q = deparenthesised_name;
156 while (*r && *r != ' ')
160 q = deparenthesised_name;
161 while (*r && *r != ')')
168 process->name = strdup(deparenthesised_name);
169 process->rss *= getpagesize();
172 update_total_processes();
176 process->totalmem = (float)(((float) process->rss / cur->memmax) / 10);
177 if (process->previous_user_time == ULONG_MAX)
178 process->previous_user_time = process->user_time;
179 if (process->previous_kernel_time == ULONG_MAX)
180 process->previous_kernel_time = process->kernel_time;
182 /* store the difference of the user_time */
183 user_time = process->user_time - process->previous_user_time;
184 kernel_time = process->kernel_time - process->previous_kernel_time;
186 /* backup the process->user_time for next time around */
187 process->previous_user_time = process->user_time;
188 process->previous_kernel_time = process->kernel_time;
190 /* store only the difference of the user_time here... */
191 process->user_time = user_time;
192 process->kernel_time = kernel_time;
198 /******************************************/
199 /* Update process table */
200 /******************************************/
202 static int update_process_table()
205 struct dirent *entry;
207 if (!(dir = opendir("/proc")))
213 * Get list of processes from /proc directory
215 while ((entry = readdir(dir))) {
220 * Problem reading list of processes
226 if (sscanf(entry->d_name, "%d", &pid) > 0) {
228 p = find_process(pid);
230 p = new_process(pid);
232 /* compute each process cpu usage */
242 /******************************************/
243 /* Get process structure for process pid */
244 /******************************************/
247 * This function seems to hog all of the CPU time. I can't figure out why - it
250 static int calculate_cpu(struct process *process)
254 /* compute each process cpu usage by reading /proc/<proc#>/stat */
255 rc = process_parse_stat(process);
258 /*rc = process_parse_statm(process);
263 * Check name against the exclusion list
265 if (process->counted && exclusion_expression
266 && !regexec(exclusion_expression, process->name, 0, 0, 0))
267 process->counted = 0;
272 /******************************************/
273 /* Strip dead process entries */
274 /******************************************/
276 static void process_cleanup()
279 struct process *p = first_process;
281 struct process *current = p;
283 #if defined(PARANOID)
284 assert(p->id == 0x0badfeed);
285 #endif /* defined(PARANOID) */
289 * Delete processes that have died
291 if (current->time_stamp != g_time)
292 delete_process(current);
296 /******************************************/
297 /* Destroy and remove a process */
298 /******************************************/
300 static void delete_process(struct process *p)
302 #if defined(PARANOID)
303 assert(p->id == 0x0badfeed);
306 * Ensure that deleted processes aren't reused.
309 #endif /* defined(PARANOID) */
312 * Maintain doubly linked list.
315 p->next->previous = p->previous;
317 p->previous->next = p->next;
319 first_process = p->next;
326 /******************************************/
327 /* Calculate cpu total */
328 /******************************************/
330 static unsigned long calc_cpu_total()
332 unsigned long total = 0;
336 char line[BUFFER_LEN];
337 unsigned long cpu = 0;
338 unsigned long nice = 0;
339 unsigned long system = 0;
340 unsigned long idle = 0;
342 ps = open("/proc/stat", O_RDONLY);
343 rc = read(ps, line, sizeof(line));
347 sscanf(line, "%*s %lu %lu %lu %lu", &cpu, &nice, &system, &idle);
348 total = cpu + nice + system + idle;
350 t = total - previous_total;
351 previous_total = total;
357 /******************************************/
358 /* Calculate each processes cpu */
359 /******************************************/
361 inline static void calc_cpu_each(unsigned long total)
363 struct process *p = first_process;
365 /*p->amount = total ?
366 (100.0 * (float) (p->user_time + p->kernel_time) /
369 (100.0 * (p->user_time + p->kernel_time) / total);
371 /* if (p->amount > 100)
377 /******************************************/
378 /* Find the top processes */
379 /******************************************/
383 * cpu comparison function for insert_sp_element
385 int compare_cpu(struct process *a, struct process *b) {
386 if (a->amount < b->amount) return 1;
391 * mem comparison function for insert_sp_element
393 int compare_mem(struct process *a, struct process *b) {
394 if (a->totalmem < b->totalmem) return 1;
399 * insert this process into the list in a sorted fashion,
400 * or destroy it if it doesn't fit on the list
402 int insert_sp_element(
403 struct sorted_process * sp_cur
404 , struct sorted_process ** p_sp_head
405 , struct sorted_process ** p_sp_tail
407 , int (*compare_funct) (struct process *, struct process *)
410 struct sorted_process * sp_readthru=NULL, * sp_destroy=NULL;
411 int did_insert = 0, x = 0;
413 if (*p_sp_head == NULL) {
418 for(sp_readthru=*p_sp_head, x=0; sp_readthru != NULL && x < max_elements; sp_readthru=sp_readthru->less, x++) {
419 if (compare_funct(sp_readthru->proc, sp_cur->proc) && !did_insert) {
420 /* sp_cur is bigger than sp_readthru so insert it before sp_readthru */
421 sp_cur->less=sp_readthru;
422 if (sp_readthru == *p_sp_head) {
423 *p_sp_head = sp_cur; /* insert as the new head of the list */
425 sp_readthru->greater->less = sp_cur; /* insert inside the list */
426 sp_cur->greater = sp_readthru->greater;
428 sp_readthru->greater=sp_cur;
429 did_insert = ++x; /* element was inserted, so increase the counter */
432 if (x < max_elements && sp_readthru == NULL && !did_insert) {
433 /* sp_cur is the smallest element and list isn't full, so insert at the end */
434 (*p_sp_tail)->less=sp_cur;
435 sp_cur->greater=*p_sp_tail;
438 } else if (x==max_elements && sp_readthru != NULL) {
439 /* we inserted an element and now the list is too big by one. Destroy the smallest element */
440 sp_destroy = sp_readthru;
441 sp_readthru->greater->less = NULL;
442 *p_sp_tail = sp_readthru->greater;
446 /* sp_cur wasn't added to the sorted list, so destroy it */
453 * create a new sp_process structure
455 struct sorted_process * malloc_sp(struct process * proc) {
456 struct sorted_process * sp;
457 sp = malloc(sizeof(struct sorted_process));
465 * copy the procs in the sorted list to the array, and destroy the list
467 void sp_acopy(struct sorted_process *sp_head, struct process ** ar, int max_size) {
469 struct sorted_process * sp_cur, * sp_tmp;
473 for (x=0; x < max_size && sp_cur != NULL; x++) {
474 ar[x] = sp_cur->proc;
476 sp_cur= sp_cur->less;
481 /* ****************************************************************** */
482 /* Get a sorted list of the top cpu hogs and top mem hogs. */
483 /* Results are stored in the cpu,mem arrays in decreasing order[0-9]. */
484 /* ****************************************************************** */
486 inline void process_find_top(struct process **cpu, struct process **mem)
488 struct sorted_process *spc_head=NULL, *spc_tail=NULL, *spc_cur=NULL;
489 struct sorted_process *spm_head=NULL, *spm_tail=NULL, *spm_cur=NULL;
490 struct process *cur_proc=NULL;
491 unsigned long total =0;
493 if (!top_cpu && !top_mem) return;
495 total = calc_cpu_total(); /* calculate the total of the processor */
496 update_process_table(); /* update the table with process list */
497 calc_cpu_each(total); /* and then the percentage for each task */
498 process_cleanup(); /* cleanup list from exited processes */
500 cur_proc = first_process;
502 while (cur_proc !=NULL) {
504 spc_cur = malloc_sp(cur_proc);
505 insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP, &compare_cpu);
508 spm_cur = malloc_sp(cur_proc);
509 insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP, &compare_mem);
511 cur_proc = cur_proc->next;
513 sp_acopy(spc_head, cpu, MAX_SP);
514 sp_acopy(spm_head, mem, MAX_SP);