1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
|
/* profile - profile operating system
*
* The profile command is used to control statistical profiling.
* It writes the profiling data collected by the kernel to a file.
*
* Changes:
* 14 Aug, 2006 Created (Rogier Meurs)
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#undef SPROFILE
#define SPROFILE 1
#include <minix/profile.h>
#define EHELP 1
#define ESYNTAX 2
#define EMEM 3
#define EOUTFILE 4
#define EFREQ 5
#define EACTION 6
#define START 1
#define STOP 2
#define DEF_OUTFILE "profile.stat.out"
#define NPIPE "/tmp/profile.npipe"
#define MIN_MEMSIZE 1
#define DEF_MEMSIZE 64
#define MIN_FREQ 3
#define MAX_FREQ 15
#define DEF_FREQ 6
#define BUFSIZE 1024
#define MB (1024*1024)
#define SYNCING "SYNC"
#define DEV_LOG "/dev/log"
int action = 0;
int mem_size = 0;
int mem_used = 0;
int freq = 0;
int intr_type = PROF_RTC;
char *outfile = "";
char *mem_ptr;
int outfile_fd, npipe_fd;
struct sprof_info_s sprof_info;
#define HASH_MOD 128
struct sproc {
endpoint_t ep;
char name[8];
struct sproc * next;
};
static struct sproc * proc_hash[HASH_MOD];
int handle_args(int argc, char *argv[]);
int start(void);
int stop(void);
int create_named_pipe(void);
int alloc_mem(void);
int init_outfile(void);
int write_outfile(void);
int write_outfile_sprof(void);
void detach(void);
int main(int argc, char *argv[])
{
int res;
if ((res = handle_args(argc, argv))) {
switch(res) {
case ESYNTAX:
printf("Error in parameters.\n");
return 1;
break;
case EACTION:
printf("Specify one of start|stop|get|reset.\n");
return 1;
break;
case EMEM:
printf("Incorrect memory size.\n");
return 1;
break;
case EFREQ:
printf("Incorrect frequency.\n");
return 1;
break;
case EOUTFILE:
printf("Output filename missing.\n");
return 1;
break;
default:
break;
}
/*
* Check the frequency when we know the intr type. Only selected values
* are correct for RTC
*/
if (action == START && intr_type == PROF_RTC &&
(freq < MIN_FREQ || freq > MAX_FREQ)) {
printf("Incorrect frequency.\n");
return 1;
}
printf("Usage:\n");
printf(" profile start [--rtc | --nmi] "
"[-m memsize] [-o outfile] [-f frequency]\n");
printf(" profile stop\n\n");
printf(" - --rtc is default, --nmi allows kernel profiling\n");
printf(" - memsize in MB, default: %u\n", DEF_MEMSIZE);
printf(" - default output file: profile.stat.out\n");
printf(" - sample frequencies for --rtc (default: %u):\n", DEF_FREQ);
printf(" 3 8192 Hz 10 64 Hz\n");
printf(" 4 4096 Hz 11 32 Hz\n");
printf(" 5 2048 Hz 12 16 Hz\n");
printf(" 6 1024 Hz 13 8 Hz\n");
printf(" 7 512 Hz 14 4 Hz\n");
printf(" 8 256 Hz 15 2 Hz\n");
printf(" 9 128 Hz\n\n");
printf("Use sprofalyze to analyze output file.\n");
return 1;
}
switch(action) {
case START:
if (start()) return 1;
break;
case STOP:
if (stop()) return 1;
break;
default:
break;
}
return 0;
}
int handle_args(int argc, char *argv[])
{
while (--argc) {
++argv;
if (strcmp(*argv, "-h") == 0 || strcmp(*argv, "help") == 0 ||
strcmp(*argv, "--help") == 0) {
return EHELP;
} else
if (strcmp(*argv, "-m") == 0) {
if (--argc == 0) return ESYNTAX;
if (sscanf(*++argv, "%u", &mem_size) != 1 ||
mem_size < MIN_MEMSIZE ) return EMEM;
} else
if (strcmp(*argv, "-f") == 0) {
if (--argc == 0) return ESYNTAX;
if (sscanf(*++argv, "%u", &freq) != 1)
return EFREQ;
} else
if (strcmp(*argv, "-o") == 0) {
if (--argc == 0) return ESYNTAX;
outfile = *++argv;
} else
if (strcmp(*argv, "--rtc") == 0) {
intr_type = PROF_RTC;
} else
if (strcmp(*argv, "--nmi") == 0) {
intr_type = PROF_NMI;
} else
if (strcmp(*argv, "start") == 0) {
if (action) return EACTION;
action = START;
} else
if (strcmp(*argv, "stop") == 0) {
if (action) return EACTION;
action = STOP;
}
}
/* No action specified. */
if (!action) return EHELP;
/* Init unspecified parameters. */
if (action == START) {
if (strcmp(outfile, "") == 0) outfile = DEF_OUTFILE;
if (mem_size == 0) mem_size = DEF_MEMSIZE;
mem_size *= MB; /* mem_size in bytes */
mem_size -= mem_size % sizeof(struct sprof_sample); /* align to sample size */
if (freq == 0) freq = DEF_FREQ; /* default frequency */
}
return 0;
}
int start()
{
/* This is the "starter process" for statistical profiling.
*
* Create output file for profiling data. Create named pipe to
* synchronize with stopper process. Fork so the parent can exit.
* Allocate memory for profiling data. Start profiling in kernel.
* Complete detachment from terminal. Write known string to named
* pipe, which blocks until read by stopper process. Redirect
* stdout/stderr to the named pipe. Write profiling data to file.
* Clean up.
*/
int log_fd;
if (init_outfile() || create_named_pipe()) return 1;
printf("Starting statistical profiling.\n");
if (fork() != 0) exit(0);
if (alloc_mem()) return 1;
if (sprofile(PROF_START, mem_size, freq, intr_type, &sprof_info, mem_ptr)) {
perror("sprofile");
fprintf(stderr, "Error starting profiling.\n");
return 1;
}
detach();
/* Temporarily redirect to system log to catch errors. */
log_fd = open(DEV_LOG, O_WRONLY);
dup2(log_fd, 1);
dup2(log_fd, 2);
if ((npipe_fd = open(NPIPE, O_WRONLY)) < 0) {
fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE);
return 1;
} else
/* Synchronize with stopper process. */
write(npipe_fd, SYNCING, strlen(SYNCING));
/* Now redirect to named pipe. */
dup2(npipe_fd, 1);
dup2(npipe_fd, 2);
mem_used = sprof_info.mem_used;
if (mem_used == -1) {
fprintf(stderr, "WARNING: Profiling was stopped prematurely due to ");
fprintf(stderr, "insufficient memory.\n");
fprintf(stderr, "Try increasing available memory using the -m switch.\n");
}
if (write_outfile()) return 1;
close(log_fd);
close(npipe_fd);
unlink(NPIPE);
close(outfile_fd);
free(mem_ptr);
return 0;
}
int stop()
{
/* This is the "stopper" process for statistical profiling.
*
* Stop profiling in kernel. Read known string from named pipe
* to synchronize with starter proces. Read named pipe until EOF
* and write to stdout, this allows feedback from started process
* to be printed.
*/
int n;
char buf[BUFSIZE];
if (sprofile(PROF_STOP, 0, 0, 0, 0, 0)) {
perror("sprofile");
fprintf(stderr, "Error stopping profiling.\n");
return 1;
} else printf("Statistical profiling stopped.\n");
if ((npipe_fd = open(NPIPE, O_RDONLY)) < 0) {
fprintf(stderr, "Unable to open named pipe %s.\n", NPIPE);
return 1;
} else
/* Synchronize with starter process. */
read(npipe_fd, buf, strlen(SYNCING));
while ((n = read(npipe_fd, buf, BUFSIZE)) > 0)
write(1, buf, n);
close(npipe_fd);
return 0;
}
int alloc_mem()
{
if ((mem_ptr = malloc(mem_size)) == 0) {
fprintf(stderr, "Unable to allocate memory.\n");
fprintf(stderr, "Used chmem to increase available proces memory?\n");
return 1;
} else memset(mem_ptr, '\0', mem_size);
return 0;
}
int init_outfile()
{
if ((outfile_fd = open(outfile, O_CREAT | O_TRUNC | O_WRONLY)) <= 0) {
fprintf(stderr, "Unable to create outfile %s.\n", outfile);
return 1;
} else chmod(outfile, S_IRUSR | S_IWUSR);
return 0;
}
int create_named_pipe()
{
if ((mkfifo(NPIPE, S_IRUSR | S_IWUSR) == -1) && (errno != EEXIST)) {
fprintf(stderr, "Unable to create named pipe %s.\n", NPIPE);
return 1;
} else
return 0;
}
void detach()
{
setsid();
(void) chdir("/");
close(0);
close(1);
close(2);
}
static void add_proc(struct sprof_proc * p)
{
struct sproc * n;
int slot = ((unsigned)(p->proc)) % HASH_MOD;
n = malloc(sizeof(struct sproc));
if (!n)
abort();
n->ep = p->proc;
memcpy(n->name, p->name, 8);
n->next = proc_hash[slot];
proc_hash[slot] = n;
}
static char * get_proc_name(endpoint_t ep)
{
struct sproc * p;
for (p = proc_hash[((unsigned)ep) % HASH_MOD]; p; p = p->next) {
if (p->ep == ep)
return p->name;
}
return NULL;
}
int write_outfile()
{
ssize_t n;
int written;
char header[80];
printf("Writing to %s ...", outfile);
/* Write header. */
sprintf(header, "stat\n%u %u %u\n", sizeof(struct sprof_info_s),
sizeof(struct sprof_sample),
sizeof(struct sprof_proc));
n = write(outfile_fd, header, strlen(header));
if (n < 0) {
fprintf(stderr, "Error writing to outfile %s.\n", outfile);
return 1;
}
written = write_outfile_sprof();
if (written < 0) return -1;
printf(" header %d bytes, data %d bytes.\n", strlen(header), written);
return 0;
}
int write_outfile_sprof()
{
int towrite;
ssize_t written, written_total = 0;
/* write profiling totals */
written = write(outfile_fd, &sprof_info, sizeof(sprof_info));
if (written != sizeof(sprof_info)) goto error;
written_total += written;
/* write raw samples */
towrite = mem_used == -1 ? mem_size : mem_used;
written = write(outfile_fd, mem_ptr, towrite);
if (written != towrite) goto error;
written_total += written;
return written_total;
error:
fprintf(stderr, "Error writing to outfile %s.\n", outfile);
return -1;
}
|