Initial revision
[monky] / top.c
1 #include "top.h"
2
3 static regex_t *exclusion_expression = 0;
4 static unsigned int g_time = 0;
5 static int previous_total = 0;
6 static struct process *first_process = 0;
7
8 static struct process *find_process(pid_t pid)
9 {
10         struct process *p = first_process;
11         while (p) {
12                 if (p->pid == pid)
13                         return p;
14                 p = p->next;
15         }
16         return 0;
17 }
18
19 /*
20  * Create a new process object and insert it into the process list
21  */
22 static struct process *new_process(int p)
23 {
24         struct process *process;
25         process = malloc(sizeof(struct process));
26
27         /*
28          * Do stitching necessary for doubly linked list
29          */
30         process->name = 0;
31         process->previous = 0;
32         process->next = first_process;
33         if (process->next)
34                 process->next->previous = process;
35         first_process = process;
36
37         process->pid = p;
38         process->time_stamp = 0;
39         process->previous_user_time = INT_MAX;
40         process->previous_kernel_time = INT_MAX;
41         process->counted = 1;
42
43 /*    process_find_name(process);*/
44
45         return process;
46 }
47
48 /******************************************/
49 /* Functions                              */
50 /******************************************/
51
52 static int process_parse_stat(struct process *);
53 static int update_process_table(void);
54 static int calculate_cpu(struct process *);
55 static void process_cleanup(void);
56 static void delete_process(struct process *);
57 /*inline void draw_processes(void);*/
58 static int calc_cpu_total(void);
59 static void calc_cpu_each(int);
60 void process_find_top(struct process **);
61
62
63 /******************************************/
64 /* Extract information from /proc         */
65 /******************************************/
66
67 /*
68  * These are the guts that extract information out of /proc.
69  * Anyone hoping to port wmtop should look here first.
70  */
71 static int process_parse_stat(struct process *process)
72 {
73         struct information *cur;
74         cur = &info;
75         char line[BUFFER_LEN], filename[BUFFER_LEN], procname[BUFFER_LEN];
76         int ps;
77         int user_time, kernel_time;
78         int rc;
79         char *r, *q;
80         char deparenthesised_name[BUFFER_LEN];
81         int endl;
82         int nice_val;
83
84         snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
85                  process->pid);
86
87         ps = open(filename, O_RDONLY);
88         if (ps < 0)
89                 /*
90                 * The process must have finished in the last few jiffies!
91                 */
92                 return 1;
93
94         /*
95         * Mark process as up-to-date.
96         */
97         process->time_stamp = g_time;
98
99         rc = read(ps, line, sizeof(line));
100         close(ps);
101         if (rc < 0)
102                 return 1;
103
104         /*
105         * Extract cpu times from data in /proc filesystem
106         */
107         rc = sscanf(line,
108                     "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %d %d %*s %*s %*s %d %*s %*s %*s %d %d",
109                     procname, &process->user_time, &process->kernel_time,
110                     &nice_val, &process->vsize, &process->rss);
111         if (rc < 5)
112                 return 1;
113         /*
114         * Remove parentheses from the process name stored in /proc/ under Linux...
115         */
116         r = procname + 1;
117         /* remove any "kdeinit: " */
118         if (r == strstr(r, "kdeinit")) {
119                 snprintf(filename, sizeof(filename),
120                          PROCFS_CMDLINE_TEMPLATE, process->pid);
121
122                 ps = open(filename, O_RDONLY);
123                 if (ps < 0)
124                         /*
125                         * The process must have finished in the last few jiffies!
126                         */
127                         return 1;
128
129                 endl = read(ps, line, sizeof(line));
130                 close(ps);
131
132                 /* null terminate the input */
133                 line[endl] = 0;
134                 /* account for "kdeinit: " */
135                 if ((char *) line == strstr(line, "kdeinit: "))
136                         r = ((char *) line) + 9;
137                 else
138                         r = (char *) line;
139
140                 q = deparenthesised_name;
141                 /* stop at space */
142                 while (*r && *r != ' ')
143                         *q++ = *r++;
144                 *q = 0;
145         } else {
146                 q = deparenthesised_name;
147                 while (*r && *r != ')')
148                         *q++ = *r++;
149                 *q = 0;
150         }
151         
152         if (process->name)
153                 free(process->name);
154         process->name = strdup(deparenthesised_name);
155         process->rss *= getpagesize();
156         
157         if(!cur->memmax)
158                 update_total_processes();
159         
160         process->totalmem = ( (float) process->rss / cur->memmax ) / 10;
161
162         if (process->previous_user_time == INT_MAX)
163                 process->previous_user_time = process->user_time;
164         if (process->previous_kernel_time == INT_MAX)
165                 process->previous_kernel_time = process->kernel_time;
166
167         /* store the difference of the user_time */
168         user_time = process->user_time - process->previous_user_time;
169         kernel_time = process->kernel_time - process->previous_kernel_time;
170
171         /* backup the process->user_time for next time around */
172         process->previous_user_time = process->user_time;
173         process->previous_kernel_time = process->kernel_time;
174
175         /* store only the difference of the user_time here... */
176         process->user_time = user_time;
177         process->kernel_time = kernel_time;
178
179
180         return 0;
181 }
182
183 /******************************************/
184 /* Update process table                   */
185 /******************************************/
186
187 static int update_process_table()
188 {
189         DIR *dir;
190         struct dirent *entry;
191
192         if (!(dir = opendir("/proc")))
193                 return 1;
194
195         ++g_time;
196
197         /*
198          * Get list of processes from /proc directory
199          */
200         while ((entry = readdir(dir))) {
201                 pid_t pid;
202
203                 if (!entry) {
204                         /*
205                          * Problem reading list of processes
206                          */
207                         closedir(dir);
208                         return 1;
209                 }
210
211                 if (sscanf(entry->d_name, "%d", &pid) > 0) {
212                         struct process *p;
213                         p = find_process(pid);
214                         if (!p)
215                                 p = new_process(pid);
216
217                         /* compute each process cpu usage */
218                         calculate_cpu(p);
219                 }
220         }
221
222         closedir(dir);
223
224         return 0;
225 }
226
227 /******************************************/
228 /* Get process structure for process pid  */
229 /******************************************/
230
231 /*
232  * This function seems to hog all of the CPU time. I can't figure out why - it
233  * doesn't do much.
234  */
235 static int calculate_cpu(struct process *process)
236 {
237         int rc;
238
239         /* compute each process cpu usage by reading /proc/<proc#>/stat */
240         rc = process_parse_stat(process);
241         if (rc)
242                 return 1;
243         /*rc = process_parse_statm(process);
244         if (rc)
245         return 1;*/
246
247         /*
248          * Check name against the exclusion list
249          */
250         if (process->counted && exclusion_expression
251             && !regexec(exclusion_expression, process->name, 0, 0, 0))
252         process->counted = 0;
253
254         return 0;
255 }
256
257 /******************************************/
258 /* Strip dead process entries             */
259 /******************************************/
260
261 static void process_cleanup()
262 {
263
264         struct process *p = first_process;
265         while (p) {
266                 struct process *current = p;
267
268 #if defined(PARANOID)
269                 assert(p->id == 0x0badfeed);
270 #endif                          /* defined(PARANOID) */
271
272                 p = p->next;
273                 /*
274                  * Delete processes that have died
275                  */
276                 if (current->time_stamp != g_time)
277                         delete_process(current);
278         }
279 }
280
281 /******************************************/
282 /* Destroy and remove a process           */
283 /******************************************/
284
285 static void delete_process(struct process *p)
286 {
287 #if defined(PARANOID)
288         assert(p->id == 0x0badfeed);
289
290         /*
291          * Ensure that deleted processes aren't reused.
292          */
293         p->id = 0x007babe;
294 #endif                          /* defined(PARANOID) */
295
296         /*
297          * Maintain doubly linked list.
298          */
299         if (p->next)
300                 p->next->previous = p->previous;
301         if (p->previous)
302                 p->previous->next = p->next;
303         else
304                 first_process = p->next;
305
306         if (p->name)
307                 free(p->name);
308         free(p);
309 }
310
311 /******************************************/
312 /* Calculate cpu total                    */
313 /******************************************/
314
315 static int calc_cpu_total()
316 {
317         int total, t;
318         int rc;
319         int ps;
320         char line[BUFFER_LEN];
321         int cpu, nice, system, idle;
322
323         ps = open("/proc/stat", O_RDONLY);
324         rc = read(ps, line, sizeof(line));
325         close(ps);
326         if (rc < 0)
327                 return 0;
328         sscanf(line, "%*s %d %d %d %d", &cpu, &nice, &system, &idle);
329         total = cpu + nice + system + idle;
330
331         t = total - previous_total;
332         previous_total = total;
333
334         if (t < 0)
335                 t = 0;
336
337         return t;
338 }
339
340 /******************************************/
341 /* Calculate each processes cpu           */
342 /******************************************/
343
344 inline static void calc_cpu_each(int total)
345 {
346         struct process *p = first_process;
347         while (p) {
348                 /*p->amount = total ?
349                     (100.0 * (float) (p->user_time + p->kernel_time) /
350                      total) : 0;*/
351                      p->amount = (100.0 * (p->user_time + p->kernel_time) / total);
352
353 /*              if (p->amount > 100)
354                      p->amount = 0;*/
355                 p = p->next;
356         }
357 }
358
359 /******************************************/
360 /* Find the top processes                 */
361 /******************************************/
362
363 /*
364  * Result is stored in decreasing order in best[0-9].
365  */
366
367 static struct process **sorttmp;
368
369 inline void process_find_top(struct process **best)
370 {
371         struct process *pr;
372         if (sorttmp == NULL) {
373                 sorttmp = malloc(sizeof(struct process)*10);
374                 assert(sorttmp != NULL);
375         }
376         int total;
377         int i, max;
378
379         total = calc_cpu_total();       /* calculate the total of the processor */
380
381         update_process_table(); /* update the table with process list */
382         calc_cpu_each(total);   /* and then the percentage for each task */
383         process_cleanup();      /* cleanup list from exited processes */
384
385         /*
386          * this is really ugly,
387          * not to mention probably not too efficient.
388          * the main problem is that there could be any number of processes,
389          * however we have to use a fixed size for the "best" array.
390          * right now i can't think of a better way to do this,
391          * although i'm sure there is one.
392          * Perhaps just using a linked list would be more effecient?
393          * I'm too fucking lazy to do that right now.
394          */
395         pr = first_process;
396         i = 0;
397         while(pr) {
398                 if(i<300 && pr->counted) {
399                         sorttmp[i] = pr;
400                         i++;
401                 }
402                 else if (i>299) {
403                         /*ERR("too many processes, you will get innaccurate results from top");*/
404                         break;
405                 }
406                 pr = pr->next;
407         }
408         max = i;
409         if(top_sort_cpu) {
410         for(i=0;i<max-1;i++)
411         {
412                 while (sorttmp[i+1]->amount > sorttmp[i]->amount)
413                 {
414                         pr = sorttmp[i];
415                         sorttmp[i] = sorttmp[i+1];
416                         sorttmp[i+1] = pr;
417                         if (i>0)
418                                 i--;
419                         else
420                                 break;
421                 }
422
423         }
424         for(i=max;i>1;i--);
425         {
426                 while (sorttmp[i]->amount > sorttmp[i-1]->amount)
427                 {
428                         pr = sorttmp[i];
429                         sorttmp[i] = sorttmp[i-1];
430                         sorttmp[i-1] = pr;
431                         if (i<max)
432                                 i++;
433                         else
434                                 break;
435                 }
436         }
437         for(i=0;i<10;i++)
438         {
439                 best[i] = sorttmp[i];
440
441         }
442         }
443         else {
444                 for(i=0;i<max-1;i++)
445                 {
446                         while (sorttmp[i+1]->totalmem > sorttmp[i]->totalmem)
447                         {
448                                 pr = sorttmp[i];
449                                 sorttmp[i] = sorttmp[i+1];
450                                 sorttmp[i+1] = pr;
451                                 if (i>0)
452                                         i--;
453                                 else
454                                         break;
455                         }
456
457                 }
458                 for(i=max;i>1;i--);
459                 {
460                         while (sorttmp[i]->totalmem > sorttmp[i-1]->totalmem)
461                         {
462                                 pr = sorttmp[i];
463                                 sorttmp[i] = sorttmp[i-1];
464                                 sorttmp[i-1] = pr;
465                                 if (i<max)
466                                         i++;
467                                 else
468                                         break;
469                         }
470                 }
471                 for(i=0;i<10;i++)
472                 {
473                         best[i] = sorttmp[i];
474
475                 }
476         }
477 }