dum de dum
[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
61
62 /******************************************/
63 /* Extract information from /proc         */
64 /******************************************/
65
66 /*
67 * These are the guts that extract information out of /proc.
68 * Anyone hoping to port wmtop should look here first.
69 */
70 static int process_parse_stat(struct process *process)
71 {
72         struct information *cur;
73         cur = &info;
74         char line[BUFFER_LEN], filename[BUFFER_LEN], procname[BUFFER_LEN];
75         int ps;
76         int user_time, kernel_time;
77         int rc;
78         char *r, *q;
79         char deparenthesised_name[BUFFER_LEN];
80         int endl;
81         int nice_val;
82
83         snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
84                  process->pid);
85
86         ps = open(filename, O_RDONLY);
87         if (ps < 0)
88                 /*
89                 * The process must have finished in the last few jiffies!
90                 */
91                 return 1;
92
93         /*
94         * Mark process as up-to-date.
95         */
96         process->time_stamp = g_time;
97
98         rc = read(ps, line, sizeof(line));
99         close(ps);
100         if (rc < 0)
101                 return 1;
102
103         /*
104         * Extract cpu times from data in /proc filesystem
105         */
106         rc = sscanf(line,
107                     "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %d %d %*s %*s %*s %d %*s %*s %*s %d %d",
108                     procname, &process->user_time, &process->kernel_time,
109                     &nice_val, &process->vsize, &process->rss);
110         if (rc < 5)
111                 return 1;
112         /*
113         * Remove parentheses from the process name stored in /proc/ under Linux...
114         */
115         r = procname + 1;
116         /* remove any "kdeinit: " */
117         if (r == strstr(r, "kdeinit")) {
118                 snprintf(filename, sizeof(filename),
119                          PROCFS_CMDLINE_TEMPLATE, process->pid);
120
121                 ps = open(filename, O_RDONLY);
122                 if (ps < 0)
123                         /*
124                         * The process must have finished in the last few jiffies!
125                         */
126                         return 1;
127
128                 endl = read(ps, line, sizeof(line));
129                 close(ps);
130
131                 /* null terminate the input */
132                 line[endl] = 0;
133                 /* account for "kdeinit: " */
134                 if ((char *) line == strstr(line, "kdeinit: "))
135                         r = ((char *) line) + 9;
136                 else
137                         r = (char *) line;
138
139                 q = deparenthesised_name;
140                 /* stop at space */
141                 while (*r && *r != ' ')
142                         *q++ = *r++;
143                 *q = 0;
144         } else {
145                 q = deparenthesised_name;
146                 while (*r && *r != ')')
147                         *q++ = *r++;
148                 *q = 0;
149         }
150         
151         if (process->name)
152                 free(process->name);
153         process->name = strdup(deparenthesised_name);
154         process->rss *= getpagesize();
155         
156         if(!cur->memmax)
157                 update_total_processes();
158         
159         process->totalmem = ( (float) process->rss / cur->memmax ) / 10;
160
161         if (process->previous_user_time == INT_MAX)
162                 process->previous_user_time = process->user_time;
163         if (process->previous_kernel_time == INT_MAX)
164                 process->previous_kernel_time = process->kernel_time;
165
166         /* store the difference of the user_time */
167         user_time = process->user_time - process->previous_user_time;
168         kernel_time = process->kernel_time - process->previous_kernel_time;
169
170         /* backup the process->user_time for next time around */
171         process->previous_user_time = process->user_time;
172         process->previous_kernel_time = process->kernel_time;
173
174         /* store only the difference of the user_time here... */
175         process->user_time = user_time;
176         process->kernel_time = kernel_time;
177
178
179         return 0;
180 }
181
182 /******************************************/
183 /* Update process table                   */
184 /******************************************/
185
186 static int update_process_table()
187 {
188         DIR *dir;
189         struct dirent *entry;
190
191         if (!(dir = opendir("/proc")))
192                 return 1;
193
194         ++g_time;
195
196         /*
197         * Get list of processes from /proc directory
198         */
199         while ((entry = readdir(dir))) {
200                 pid_t pid;
201
202                 if (!entry) {
203                         /*
204                         * Problem reading list of processes
205                         */
206                         closedir(dir);
207                         return 1;
208                 }
209
210                 if (sscanf(entry->d_name, "%d", &pid) > 0) {
211                         struct process *p;
212                         p = find_process(pid);
213                         if (!p)
214                                 p = new_process(pid);
215
216                         /* compute each process cpu usage */
217                         calculate_cpu(p);
218                 }
219         }
220
221         closedir(dir);
222
223         return 0;
224 }
225
226 /******************************************/
227 /* Get process structure for process pid  */
228 /******************************************/
229
230 /*
231 * This function seems to hog all of the CPU time. I can't figure out why - it
232 * doesn't do much.
233 */
234 static int calculate_cpu(struct process *process)
235 {
236         int rc;
237
238         /* compute each process cpu usage by reading /proc/<proc#>/stat */
239         rc = process_parse_stat(process);
240         if (rc)
241                 return 1;
242         /*rc = process_parse_statm(process);
243         if (rc)
244         return 1;*/
245
246         /*
247         * Check name against the exclusion list
248         */
249         if (process->counted && exclusion_expression
250                    && !regexec(exclusion_expression, process->name, 0, 0, 0))
251                 process->counted = 0;
252
253         return 0;
254 }
255
256 /******************************************/
257 /* Strip dead process entries             */
258 /******************************************/
259
260 static void process_cleanup()
261 {
262
263         struct process *p = first_process;
264         while (p) {
265                 struct process *current = p;
266
267 #if defined(PARANOID)
268                 assert(p->id == 0x0badfeed);
269 #endif                          /* defined(PARANOID) */
270
271                 p = p->next;
272                 /*
273                 * Delete processes that have died
274                 */
275                 if (current->time_stamp != g_time)
276                         delete_process(current);
277         }
278 }
279
280 /******************************************/
281 /* Destroy and remove a process           */
282 /******************************************/
283
284 static void delete_process(struct process *p)
285 {
286 #if defined(PARANOID)
287         assert(p->id == 0x0badfeed);
288
289         /*
290         * Ensure that deleted processes aren't reused.
291         */
292         p->id = 0x007babe;
293 #endif                          /* defined(PARANOID) */
294
295         /*
296         * Maintain doubly linked list.
297         */
298         if (p->next)
299                 p->next->previous = p->previous;
300         if (p->previous)
301                 p->previous->next = p->next;
302         else
303                 first_process = p->next;
304
305         if (p->name)
306                 free(p->name);
307         free(p);
308 }
309
310 /******************************************/
311 /* Calculate cpu total                    */
312 /******************************************/
313
314 static int calc_cpu_total()
315 {
316         int total, t;
317         int rc;
318         int ps;
319         char line[BUFFER_LEN];
320         int cpu, nice, system, idle;
321
322         ps = open("/proc/stat", O_RDONLY);
323         rc = read(ps, line, sizeof(line));
324         close(ps);
325         if (rc < 0)
326                 return 0;
327         sscanf(line, "%*s %d %d %d %d", &cpu, &nice, &system, &idle);
328         total = cpu + nice + system + idle;
329
330         t = total - previous_total;
331         previous_total = total;
332
333         if (t < 0)
334                 t = 0;
335
336         return t;
337 }
338
339 /******************************************/
340 /* Calculate each processes cpu           */
341 /******************************************/
342
343 inline static void calc_cpu_each(int total)
344 {
345         struct process *p = first_process;
346         while (p) {
347                 /*p->amount = total ?
348                 (100.0 * (float) (p->user_time + p->kernel_time) /
349                 total) : 0;*/
350                 p->amount = (100.0 * (p->user_time + p->kernel_time) / total);
351
352 /*              if (p->amount > 100)
353                 p->amount = 0;*/
354                 p = p->next;
355         }
356 }
357
358 /******************************************/
359 /* Find the top processes                 */
360 /******************************************/
361
362 /*
363 * Result is stored in decreasing order in best[0-9].
364 */
365
366 static struct process **sorttmp;
367 static size_t sorttmp_size = 10;
368
369 inline void process_find_top(struct process **cpu, struct process **mem)
370 {
371         struct process *pr;
372         if (sorttmp == NULL) {
373                 sorttmp = malloc(sizeof(struct process)*sorttmp_size);
374                 assert(sorttmp != NULL);
375         }
376         int total;
377         unsigned 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         if(top_cpu) {
396                 pr = first_process;
397                 i = 0;
398                 while(pr) {
399                         if(i<sorttmp_size && pr->counted) {
400                                 sorttmp[i] = pr;
401                                 i++;
402                         }
403                         else if (i == sorttmp_size && pr->counted) {
404                                 sorttmp_size++;
405                                 sorttmp = realloc(sorttmp, sizeof(struct process)*sorttmp_size);
406                                 sorttmp[i] = pr;
407                                 i++;
408                         }
409                         pr = pr->next;
410                 }
411                 if (i+1 < sorttmp_size) {
412                         sorttmp = realloc(sorttmp, sizeof(struct process)*sorttmp_size);
413                 }
414                 max = i;
415                 for(i=0;i<max-1;i++)
416                 {
417                         while (sorttmp[i+1]->amount > sorttmp[i]->amount)
418                         {
419                                 pr = sorttmp[i];
420                                 sorttmp[i] = sorttmp[i+1];
421                                 sorttmp[i+1] = pr;
422                                 if (i>0)
423                                         i--;
424                                 else
425                                         break;
426                         }
427
428                 }
429                 for(i=max;i>1;i--);
430                 {
431                         while (sorttmp[i]->amount > sorttmp[i-1]->amount)
432                         {
433                                 pr = sorttmp[i];
434                                 sorttmp[i] = sorttmp[i-1];
435                                 sorttmp[i-1] = pr;
436                                 if (i<max)
437                                         i++;
438                                 else
439                                         break;
440                         }
441                 }
442                 for(i=0;i<10;i++)
443                 {
444                         cpu[i] = sorttmp[i];
445
446                 }
447         }
448         if (top_mem) {
449                 pr = first_process;
450                 i = 0;
451                 while(pr) {
452                         if(i<sorttmp_size && pr->counted) {
453                                 sorttmp[i] = pr;
454                                 i++;
455                         }
456                         else if (i == sorttmp_size && pr->counted) {
457                                 sorttmp_size++;
458                                 sorttmp = realloc(sorttmp, sizeof(struct process)*sorttmp_size);
459                                 sorttmp[i] = pr;
460                                 i++;
461                         }
462                         pr = pr->next;
463                 }
464                 if (i+1 < sorttmp_size) {
465                         sorttmp = realloc(sorttmp, sizeof(struct process)*sorttmp_size);
466                 }               max = i;
467                 for(i=0;i<max-1;i++)
468                 {
469                         while (sorttmp[i+1]->totalmem > sorttmp[i]->totalmem)
470                         {
471                                 pr = sorttmp[i];
472                                 sorttmp[i] = sorttmp[i+1];
473                                 sorttmp[i+1] = pr;
474                                 if (i>0)
475                                         i--;
476                                 else
477                                         break;
478                         }
479
480                 }
481                 for(i=max;i>1;i--);
482                 {
483                         while (sorttmp[i]->totalmem > sorttmp[i-1]->totalmem)
484                         {
485                                 pr = sorttmp[i];
486                                 sorttmp[i] = sorttmp[i-1];
487                                 sorttmp[i-1] = pr;
488                                 if (i<max)
489                                         i++;
490                                 else
491                                         break;
492                         }
493                 }
494                 for(i=0;i<10;i++)
495                 {
496                         mem[i] = sorttmp[i];
497
498                 }
499         }
500 }