Statistics
| Branch: | Tag: | Revision:

sssimulator / proc-time.c @ f1158e5c

History | View | Annotate | Download (8.26 KB)

1
/*
2
 proc-time.c -- Get resource usage from /proc for a command (GNU/Linux).
3

4
 awaiting PC's linux-mm patch for getrusage
5

6
 source file of the GNU LilyPond music typesetter
7
 Licence: GNU GPL
8
 
9
 (c) 2000 Jan Nieuwenhuizen <janneke@gnu.org>
10

11
 Translated from Python prototype
12

13
 GNU time doesn't report memory stuff on FreeBSD either,
14
 but I couldn't find an easy way like to get this info (like /proc).
15
*/
16
 
17
#include <getopt.h>
18
#include <stdio.h>
19

    
20
char const* name = "proc-time";
21
char const* version = "1.3.55";
22

    
23
/* + : stop parsing options when non-option is found */
24
char const* short_opts = "+bhi:ntvV";
25
struct option long_opts[] =
26
{
27
  {"heartbeat", no_argument, 0, 'b'},
28
  {"help", no_argument, 0, 'h'},
29
  {"interval", required_argument, 0, 'i'},
30
  {"no-fork", no_argument, 0, 'n'},
31
  {"test", no_argument, 0, 't'},
32
  {"version", no_argument, 0, 'v'},
33
  {"verbose", no_argument, 0, 'V'},
34
  {0, no_argument, 0, 0}
35
};
36

    
37
int test = 0;
38
int nofork = 0;
39
int heartbeat = 0;
40
float interval = 0.5;
41
int status = 0;
42
int verbose = 0;
43

    
44
void
45
identify (FILE* f)
46
{
47
  fprintf (f, "%s from LilyPond %s\n", name, version);
48
}
49

    
50
void
51
print_usage ()
52
{
53
  identify (stdout);
54
  printf ("\n"
55
          "Usage: %s [OPTION]... COMMAND\n"
56
          "\n"
57
          "Get resource usage from /proc for COMMAND (GNU/Linux).\n"
58
          "\n"
59
          "Options:\n"
60
          "  -b, --heartbeat     show memory info at every heartbeat\n"
61
          "  -h, --help          this help\n"
62
          "  -i, --interval=TIME set heartbeat to TIME seconds\n"
63
          "  -n, --no-fork       don't fork\n"
64
          "  -t, --test          test mode\n"
65
          "  -V, --verbose       be verbose\n"
66
          "  -v, --version       version information\n"
67
          "\n", name);
68
}
69

    
70
void
71
print_version ()
72
{
73
  printf ("%s (GNU LilyPond) %s", name, version);
74
}
75

    
76
#include <sys/types.h>
77
#include <errno.h>
78
#include <signal.h>
79
#include <sys/wait.h>
80
#include <sys/time.h>
81
#include <unistd.h>
82

    
83
#define JIF_TO_SEC 0.01
84
#define PAGE_SIZE 4096
85
#define PAGE_TO_MEG PAGE_SIZE / (1024 * 1024)
86
#define MAX(a, b) (a < b ? b : a)
87

    
88
enum stat_enum
89
{
90
  STAT_USER_JIFFIES = 13,
91
  STAT_SYSTEM_JIFFIES,
92
  STAT_COUNTER = 17,
93
  STAT_MAX
94
};
95

    
96
enum statm_enum
97
{
98
  STATM_SIZE = 0,
99
  STATM_RSS,
100
  STATM_SHARED,
101
  STATM_CODE,
102
  STATM_DATA,
103
  STATM_LIB,
104
  STATM_DIRTY,
105
  STATM_MAX
106
};
107

    
108
struct process_info
109
{
110
  pid_t id;
111
  int t;
112
  char stat_name[81];
113
  char statm_name[81];
114
  int stat_i[STAT_MAX];
115
  int max_size;
116
  int cum_size;
117
  int max_rss;
118
  int cum_rss;
119
  struct timeval start_tv;
120
  struct timeval stop_tv;
121
};
122

    
123
struct process_info child_pi;
124
void
125
init (struct process_info* pi, pid_t pid)
126
{
127
  pi->id = pid;
128
  pi->t = 0;
129
  snprintf (pi->statm_name, sizeof (pi->statm_name) - 1,
130
            "/proc/%d/statm", pi->id);
131
  snprintf (pi->stat_name, sizeof (pi->stat_name) - 1,
132
            "/proc/%d/stat", pi->id);
133
  gettimeofday (&pi->start_tv, 0);
134
}
135

    
136
void
137
update_mem_stats (struct process_info* pi)
138
{
139
  FILE* f = fopen (pi->statm_name, "r");
140
  if (f)
141
    {
142
      int statm_i[STATM_MAX];
143
      if (fscanf (f, "%d %d %d %d %d %d %d",
144
                  &statm_i[0], 
145
                  &statm_i[1], 
146
                  &statm_i[2], 
147
                  &statm_i[3], 
148
                  &statm_i[4], 
149
                  &statm_i[5], 
150
                  &statm_i[6])
151
          >= STATM_MAX - 1)
152
        {
153
          pi->t++;
154
          pi->max_size = MAX (statm_i[STATM_SIZE], pi->max_size);
155
          pi->cum_size += statm_i[STATM_SIZE];
156
          pi->max_rss = MAX (statm_i[STATM_RSS], pi->max_rss);
157
          pi->cum_rss += statm_i[STATM_RSS];
158
        }
159
      else
160
        fprintf (stderr, "%s: scanf failed: %s", pi->statm_name, strerror (errno));
161
      fclose (f);
162
    }
163
  else
164
    fprintf (stderr, "%s: reading failed: %s", pi->statm_name, strerror (errno));
165
}
166

    
167
void
168
print_mem_stats (struct process_info* pi)
169
{
170
  if (pi->t)
171
    {
172
      int avg_size = pi->cum_size / pi->t;
173
      int avg_rss = pi->cum_rss / pi->t;
174
      fprintf (stderr, "MAXSIZE: %6.3fM(%d), MAXRSS: %6.3fM(%d)\n",
175
               (float)pi->max_size * PAGE_TO_MEG, pi->max_size, 
176
               (float)pi->max_rss * PAGE_TO_MEG, pi->max_rss);
177
      fprintf (stderr, "AVGSIZE: %6.3fM(%d), AVGRSS: %6.3fM(%d)\n",
178
               (float)avg_size * PAGE_TO_MEG, avg_size, 
179
               (float)avg_rss * PAGE_TO_MEG, avg_rss);
180
      fflush (stdout);
181
      fflush (stderr);
182
    }
183
}
184

    
185
void
186
update_time_stats (struct process_info* pi)
187
{
188
  FILE* f = fopen (pi->stat_name, "r");
189
  if (f)
190
    {
191
      char name[80];
192
      char s[80];
193
      if (fscanf (f,
194
                  "%d %s %s %d %d "
195
                  "%d %d %d %d %d "
196
                  "%d %d %d %d %d "
197
                  "%d %d %d",
198
                  &pi->stat_i[0],
199
                  &name,
200
                  &s,
201
                  &pi->stat_i[3],
202
                  &pi->stat_i[4],
203
                  &pi->stat_i[5],
204
                  &pi->stat_i[6],
205
                  &pi->stat_i[7],
206
                  &pi->stat_i[8],
207
                  &pi->stat_i[9],
208
                  &pi->stat_i[10],
209
                  &pi->stat_i[11],
210
                  &pi->stat_i[12],
211
                  &pi->stat_i[13],
212
                  &pi->stat_i[14],
213
                  &pi->stat_i[15],
214
                  &pi->stat_i[16],
215
                  &pi->stat_i[17])
216
          >= STAT_MAX - 1)
217
        ;
218
      else
219
        fprintf (stderr, "%s: scanf failed: %s", pi->stat_name, strerror (errno));
220
      fclose (f);
221
    }
222
  gettimeofday (&pi->stop_tv, 0);
223
}
224

    
225
void
226
print_time_stats (struct process_info* pi)
227
{
228
  fprintf (stderr, "user: %6.2f(%d) system: %6.2f(%d)\n",
229
           (float)pi->stat_i[STAT_USER_JIFFIES] * JIF_TO_SEC,
230
           pi->stat_i[STAT_USER_JIFFIES],
231
           (float)pi->stat_i[STAT_SYSTEM_JIFFIES] * JIF_TO_SEC,
232
           pi->stat_i[STAT_SYSTEM_JIFFIES]);
233
  fprintf (stderr, "elapsed: %6.2f\n",
234
           (float)(pi->stop_tv.tv_sec - pi->start_tv.tv_sec)
235
                   + (pi->stop_tv.tv_usec - pi->start_tv.tv_usec) * 1e-6);
236
}
237

    
238
void
239
handler (int arg)
240
{
241
  (void)arg;
242
  /* urg: child_pi */
243
  update_time_stats (&child_pi);
244
  update_mem_stats (&child_pi);
245
  fprintf (stderr, "\n");
246
  print_time_stats (&child_pi);
247
  print_mem_stats (&child_pi);
248
  if (test)
249
    fprintf (stderr, "handler\n");
250
}
251

    
252
char**
253
getargs (int argc, char** argv)
254
{
255
  int c;
256
  while ((c = getopt_long (argc, argv, short_opts, long_opts, (int *) 0))
257
         != EOF)
258
    {
259
      switch (c)
260
        {
261
        case 'b':
262
          heartbeat = 1;
263
          break;
264
        case 'h':
265
          print_usage ();
266
          exit (0);
267
          break;
268
        case 'i':
269
          if (optarg)
270
            {
271
              float f;
272
              if (sscanf (optarg, "%f", &f))
273
                {
274
                  interval = f;
275
                }
276
            }
277
          break;
278
        case 'n':
279
          nofork = 1;
280
          break;
281
        case 't':
282
          test = 1;
283
          break;
284
        case 'v':
285
          identify (stdout);
286
          exit (0);
287
          break;
288
        case 'V':
289
          verbose = 1;
290
          break;
291
        default:
292
          print_usage ();
293
          exit (2);
294
          break;
295
        }
296
    }
297

    
298
  if (optind == argc)
299
    {
300
      print_usage ();
301
      exit (2);
302
    }
303

    
304
  return &argv[optind];
305
}
306

    
307
int
308
run (char** command)
309
{
310
  if (nofork)
311
    init (&child_pi, 1);
312
  else
313
    init (&child_pi, fork ());
314

    
315
  if (child_pi.id)
316
    {
317
      /* Parent */
318

    
319
      int input = 0;
320
      fd_set rfds;
321
      struct timeval tv;
322
      /* Watch stdin (fd 0) to see when it has input. */
323
      FD_ZERO (&rfds);
324
      FD_SET (0, &rfds);
325

    
326
      update_mem_stats (&child_pi);
327
      if (interval <= 0)
328
        {
329
          waitpid (child_pi.id, &status, 0);
330
          exit (status);
331
        }
332
      while (1)
333
        {
334
          if (waitpid (child_pi.id, &status, WNOHANG))
335
            break;
336
          
337
          /*
338
            should we use pause/alarm?
339
            when using the sleeptime of select, we don't see any input??
340
           */
341
#if 0
342
          /* man page says we should reset these */
343
          tv.tv_sec = 0;
344
          tv.tv_usec = (unsigned long)(interval*1e6);
345
#else
346
          usleep ((unsigned long)(interval*1e6));
347
          /* man page says we should reset these */
348
          tv.tv_sec = 0;
349
          tv.tv_usec = 0;
350
#endif
351
          input = select(1, &rfds, 0, 0, &tv);
352
          update_mem_stats (&child_pi);
353

    
354
          //if (input)
355
          //  {
356
          //    fprintf (stderr, "\n");
357
          //    print_mem_stats (&child_pi);
358
          //    getc (stdin);
359
          //  }
360
          //else 
361
          //if (heartbeat)
362
          //  {
363
          //    fprintf (stderr, "\n");
364
          //    print_mem_stats (&child_pi);
365
          //  }
366
        }
367
    }
368
  else
369
    {
370
      /* Child */
371
      execvp (command[0], command);
372
    }
373

    
374
  return status;
375
}
376

    
377
      
378
void
379
check_file (char const* name, char const* message)
380
{
381
  FILE* f;
382
  f = fopen (name, "r");
383
  if (f)
384
    {
385
      fclose (f);
386
    }
387
  else
388
    {
389
      fprintf (stderr, "%s: can't open: %s\n", name, message);
390
    }
391
}
392

    
393
void
394
check_sanity ()
395
{
396
  struct process_info init_pi;
397
  init (&init_pi, 1);
398

    
399
  check_file (init_pi.statm_name, "memory statistics unavailable");
400
  check_file (init_pi.stat_name, "detailed time statistics unavailable");
401
}
402

    
403
int
404
main (int argc, char** argv)
405
{ 
406
  int status = 0;
407
  char** command = getargs (argc, argv);
408
  identify (stderr);
409
  
410
  check_sanity ();
411

    
412
  signal (SIGINT, handler);
413
  signal (SIGCHLD, handler);
414
  signal (SIGTERM, handler);
415

    
416
  status = run (command);
417

    
418
  if (!status)
419
    waitpid (child_pi.id, &status, 0);
420

    
421
  if (status)
422
    fprintf (stderr, "Command exited with non-zero exit status: %d\n", status);
423

    
424
  return status;
425
}