fixed memory leak in new linked list top routine
[monky] / src / top.c
1 /*
2  * Conky, a system monitor, based on torsmo
3  *
4  * This program is licensed under BSD license, read COPYING
5  *
6  *  $Id$
7  */
8
9 #include "top.h"
10
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;
15
16 struct process *get_first_process()
17 {
18         return first_process;
19 }
20
21
22
23 void free_all_processes(struct process *pr)
24 {
25         struct process *next = NULL;
26         while (pr) {
27                 next = pr->next;
28                 if (pr->name) {
29                         free(pr->name);
30                 }
31                 free(pr);
32                 pr = next;
33         }
34 }
35
36 static struct process *find_process(pid_t pid)
37 {
38         struct process *p = first_process;
39         while (p) {
40                 if (p->pid == pid)
41                         return p;
42                 p = p->next;
43         }
44         return 0;
45 }
46
47 /*
48 * Create a new process object and insert it into the process list
49 */
50 static struct process *new_process(int p)
51 {
52         struct process *process;
53         process = (struct process*)malloc(sizeof(struct process));
54
55         // clean up memory first
56         memset(process, 0, sizeof(struct process));
57
58         /*
59          * Do stitching necessary for doubly linked list
60          */
61         process->name = 0;
62         process->previous = 0;
63         process->next = first_process;
64         if (process->next)
65                 process->next->previous = process;
66         first_process = process;
67
68         process->pid = p;
69         process->time_stamp = 0;
70         process->previous_user_time = ULONG_MAX;
71         process->previous_kernel_time = ULONG_MAX;
72         process->counted = 1;
73
74         
75         /*    process_find_name(process); */
76
77         return process;
78 }
79
80 /******************************************/
81 /* Functions                              */
82 /******************************************/
83
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);
92
93
94 /******************************************/
95 /* Extract information from /proc         */
96 /******************************************/
97
98 /*
99 * These are the guts that extract information out of /proc.
100 * Anyone hoping to port wmtop should look here first.
101 */
102 static int process_parse_stat(struct process *process)
103 {
104         struct information *cur;
105         cur = &info;
106         char line[BUFFER_LEN], filename[BUFFER_LEN], procname[BUFFER_LEN];
107         int ps;
108         unsigned long user_time = 0;
109         unsigned long kernel_time = 0;
110         int rc;
111         char *r, *q;
112         char deparenthesised_name[BUFFER_LEN];
113         int endl;
114         int nice_val;
115
116         snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
117                  process->pid);
118
119         ps = open(filename, O_RDONLY);
120         if (ps < 0)
121                 /*
122                  * The process must have finished in the last few jiffies!
123                  */
124                 return 1;
125
126         /*
127          * Mark process as up-to-date.
128          */
129         process->time_stamp = g_time;
130
131         rc = read(ps, line, sizeof(line));
132         close(ps);
133         if (rc < 0)
134                 return 1;
135
136         /*
137          * Extract cpu times from data in /proc filesystem
138          */
139         rc = sscanf(line,
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);
143         if (rc < 5)
144                 return 1;
145         /*
146          * Remove parentheses from the process name stored in /proc/ under Linux...
147          */
148         r = procname + 1;
149         /* remove any "kdeinit: " */
150         if (r == strstr(r, "kdeinit")) {
151                 snprintf(filename, sizeof(filename),
152                          PROCFS_CMDLINE_TEMPLATE, process->pid);
153
154                 ps = open(filename, O_RDONLY);
155                 if (ps < 0)
156                         /*
157                          * The process must have finished in the last few jiffies!
158                          */
159                         return 1;
160
161                 endl = read(ps, line, sizeof(line));
162                 close(ps);
163
164                 /* null terminate the input */
165                 line[endl] = 0;
166                 /* account for "kdeinit: " */
167                 if ((char *) line == strstr(line, "kdeinit: "))
168                         r = ((char *) line) + 9;
169                 else
170                         r = (char *) line;
171
172                 q = deparenthesised_name;
173                 /* stop at space */
174                 while (*r && *r != ' ')
175                         *q++ = *r++;
176                 *q = 0;
177         } else {
178                 q = deparenthesised_name;
179                 while (*r && *r != ')')
180                         *q++ = *r++;
181                 *q = 0;
182         }
183
184         if (process->name) {
185                 free(process->name);
186         }
187         process->name = strdup(deparenthesised_name);
188         process->rss *= getpagesize();
189
190         if (!cur->memmax)
191                 update_total_processes();
192
193
194
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;
200
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;
204
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;
208
209         /* store only the difference of the user_time here... */
210         process->user_time = user_time;
211         process->kernel_time = kernel_time;
212
213
214         return 0;
215 }
216
217 /******************************************/
218 /* Update process table                   */
219 /******************************************/
220
221 static int update_process_table()
222 {
223         DIR *dir;
224         struct dirent *entry;
225
226         if (!(dir = opendir("/proc")))
227                 return 1;
228
229         ++g_time;
230
231         /*
232          * Get list of processes from /proc directory
233          */
234         while ((entry = readdir(dir))) {
235                 pid_t pid;
236
237                 if (!entry) {
238                         /*
239                          * Problem reading list of processes
240                          */
241                         closedir(dir);
242                         return 1;
243                 }
244
245                 if (sscanf(entry->d_name, "%d", &pid) > 0) {
246                         struct process *p;
247                         p = find_process(pid);
248                         if (!p)
249                                 p = new_process(pid);
250
251                         /* compute each process cpu usage */
252                         calculate_cpu(p);
253                 }
254         }
255
256         closedir(dir);
257
258         return 0;
259 }
260
261 /******************************************/
262 /* Get process structure for process pid  */
263 /******************************************/
264
265 /*
266 * This function seems to hog all of the CPU time. I can't figure out why - it
267 * doesn't do much.
268 */
269 static int calculate_cpu(struct process *process)
270 {
271         int rc;
272
273         /* compute each process cpu usage by reading /proc/<proc#>/stat */
274         rc = process_parse_stat(process);
275         if (rc)
276                 return 1;
277         /*rc = process_parse_statm(process);
278            if (rc)
279            return 1; */
280
281         /*
282          * Check name against the exclusion list
283          */
284         if (process->counted && exclusion_expression
285             && !regexec(exclusion_expression, process->name, 0, 0, 0))
286                 process->counted = 0;
287
288         return 0;
289 }
290
291 /******************************************/
292 /* Strip dead process entries             */
293 /******************************************/
294
295 static void process_cleanup()
296 {
297
298         struct process *p = first_process;
299         while (p) {
300                 struct process *current = p;
301
302 #if defined(PARANOID)
303                 assert(p->id == 0x0badfeed);
304 #endif                          /* defined(PARANOID) */
305
306                 p = p->next;
307                 /*
308                  * Delete processes that have died
309                  */
310                 if (current->time_stamp != g_time)
311                         delete_process(current);
312         }
313 }
314
315 /******************************************/
316 /* Destroy and remove a process           */
317 /******************************************/
318
319 static void delete_process(struct process *p)
320 {
321 #if defined(PARANOID)
322         assert(p->id == 0x0badfeed);
323
324         /*
325          * Ensure that deleted processes aren't reused.
326          */
327         p->id = 0x007babe;
328 #endif                          /* defined(PARANOID) */
329
330         /*
331          * Maintain doubly linked list.
332          */
333         if (p->next)
334                 p->next->previous = p->previous;
335         if (p->previous)
336                 p->previous->next = p->next;
337         else
338                 first_process = p->next;
339
340         if (p->name) {
341                 free(p->name);
342         }
343         free(p);
344 }
345
346 /******************************************/
347 /* Calculate cpu total                    */
348 /******************************************/
349
350 static unsigned long calc_cpu_total()
351 {
352         unsigned long total = 0;
353         unsigned long t = 0;
354         int rc;
355         int ps;
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;
361
362         ps = open("/proc/stat", O_RDONLY);
363         rc = read(ps, line, sizeof(line));
364         close(ps);
365         if (rc < 0)
366                 return 0;
367         sscanf(line, "%*s %lu %lu %lu %lu", &cpu, &nice, &system, &idle);
368         total = cpu + nice + system + idle;
369
370         t = total - previous_total;
371         previous_total = total;
372
373
374         return t;
375 }
376
377 /******************************************/
378 /* Calculate each processes cpu           */
379 /******************************************/
380
381 inline static void calc_cpu_each(unsigned long total)
382 {
383         struct process *p = first_process;
384         while (p) {
385                 /*p->amount = total ?
386                    (100.0 * (float) (p->user_time + p->kernel_time) /
387                    total) : 0; */
388                 p->amount =
389                     (100.0 * (p->user_time + p->kernel_time) / total);
390
391 /*              if (p->amount > 100)
392                 p->amount = 0;*/
393                 p = p->next;
394         }
395 }
396
397 /******************************************/
398 /* Find the top processes                 */
399 /******************************************/
400
401 // static int tot_struct;  //for debugging..uncomment this and the 2 printfs in the next two functs
402
403 /*
404  * free a  sp_process structure
405 */
406 void free_sp(struct sorted_process * sp) {
407         free(sp);
408         //printf("free: %d structs\n",--tot_struct );
409 }
410
411 /*
412  * create a new sp_process structure
413 */
414 struct sorted_process * malloc_sp(struct process * proc) {
415         struct sorted_process * sp;
416         sp = malloc(sizeof(struct sorted_process));
417         sp->greater = NULL;
418         sp->less = NULL;
419         sp->proc = proc;
420         //printf("malloc: %d structs\n", ++tot_struct);
421         return(sp);
422
423
424 /*
425  * cpu comparison function for insert_sp_element 
426  */
427 int compare_cpu(struct process *a, struct process *b) {
428         if (a->amount < b->amount) return 1; 
429         return 0;
430 }
431
432 /*
433  * mem comparison function for insert_sp_element 
434  */
435 int compare_mem(struct process *a, struct process *b) {
436         if (a->totalmem < b->totalmem) return 1; 
437         return 0;
438 }
439
440 /*
441  * insert this process into the list in a sorted fashion,
442  * or destroy it if it doesn't fit on the list
443 */ 
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
448                    , int max_elements
449                    , int (*compare_funct) (struct process *, struct process *)
450                   ) {
451
452         struct sorted_process * sp_readthru=NULL, * sp_destroy=NULL;
453         int did_insert = 0, x = 0;
454
455         if (*p_sp_head == NULL) {
456                 *p_sp_head = sp_cur;
457                 *p_sp_tail = sp_cur;
458                 return(1);
459         }
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 */
466                         } else {
467                                 sp_readthru->greater->less = sp_cur;  /* insert inside  the list */
468                                 sp_cur->greater = sp_readthru->greater; 
469                         }
470                         sp_readthru->greater=sp_cur;
471                         did_insert = ++x;  /* element was inserted, so increase the counter */
472                 }
473         }
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;
478                 *p_sp_tail = sp_cur;
479                 did_insert=x;
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;
485                 free_sp(sp_destroy);
486         }
487         if (!did_insert) {
488                 /* sp_cur wasn't added to the sorted list, so destroy it */
489                 free_sp(sp_cur);
490         }
491         return did_insert;
492 }
493   
494 /*
495  * copy the procs in the sorted list to the array, and destroy the list 
496  */
497 void sp_acopy(struct sorted_process *sp_head, struct process ** ar, int max_size)
498 {
499         struct sorted_process * sp_cur, * sp_tmp;
500         int x;
501         sp_cur = sp_head;
502         for (x=0; x < max_size && sp_cur != NULL; x++) {
503                 ar[x] = sp_cur->proc;   
504                 sp_tmp = sp_cur;
505                 sp_cur= sp_cur->less;
506                 free_sp(sp_tmp);        
507         }
508 }
509
510 // stole from common.c
511 #define NEED(a) ((need_mask & (1 << a)) && ((info.mask & (1 << a)) == 0))
512
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 /* ****************************************************************** */
517
518 inline void process_find_top(struct process **cpu, struct process **mem)
519 {
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;
524
525         if (!top_cpu && !top_mem) return;
526
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 */
531         
532         cur_proc = first_process;
533
534         while (cur_proc !=NULL) {
535                 //printf("\n\n cur_proc: %s %f %f\n",cur_proc->name, cur_proc->totalmem, cur_proc->amount );
536                 if (top_cpu) {
537                         spc_cur = malloc_sp(cur_proc);
538                         insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP, &compare_cpu);
539                 }
540                 if (top_mem) {
541                         spm_cur = malloc_sp(cur_proc);
542                         insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP, &compare_mem);
543                 }
544                 cur_proc = cur_proc->next;
545         }
546         sp_acopy(spc_head, cpu, MAX_SP);
547         sp_acopy(spm_head, mem, MAX_SP);
548 }
549