~john-koepi/ubuntu/trusty/golang/default

« back to all changes in this revision

Viewing changes to src/cmd/prof/main.c

  • Committer: Bazaar Package Importer
  • Author(s): Ondřej Surý
  • Date: 2011-04-20 17:36:48 UTC
  • Revision ID: james.westby@ubuntu.com-20110420173648-ifergoxyrm832trd
Tags: upstream-2011.03.07.1
Import upstream version 2011.03.07.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2009 The Go Authors. All rights reserved.
 
2
// Use of this source code is governed by a BSD-style
 
3
// license that can be found in the LICENSE file.
 
4
 
 
5
#include <u.h>
 
6
#include <time.h>
 
7
#include <libc.h>
 
8
#include <bio.h>
 
9
#include <ctype.h>
 
10
 
 
11
#define Ureg Ureg_amd64
 
12
        #include <ureg_amd64.h>
 
13
#undef Ureg
 
14
#define Ureg Ureg_x86
 
15
        #include <ureg_x86.h>
 
16
#undef Ureg
 
17
#include <mach.h>
 
18
 
 
19
char* file = "6.out";
 
20
static Fhdr fhdr;
 
21
int have_syms;
 
22
int fd;
 
23
struct Ureg_amd64 ureg_amd64;
 
24
struct Ureg_x86 ureg_x86;
 
25
int total_sec = 0;
 
26
int delta_msec = 100;
 
27
int nsample;
 
28
int nsamplethread;
 
29
 
 
30
// pprof data, stored as sequences of N followed by N PC values.
 
31
// See http://code.google.com/p/google-perftools .
 
32
uvlong  *ppdata;        // traces
 
33
Biobuf* pproffd;        // file descriptor to write trace info
 
34
long    ppstart;        // start position of current trace
 
35
long    nppdata;        // length of data
 
36
long    ppalloc;        // size of allocated data
 
37
char    ppmapdata[10*1024];     // the map information for the output file
 
38
 
 
39
// output formats
 
40
int pprof;      // print pprof output to named file
 
41
int functions;  // print functions
 
42
int histograms; // print histograms
 
43
int linenums;   // print file and line numbers rather than function names
 
44
int registers;  // print registers
 
45
int stacks;             // print stack traces
 
46
 
 
47
int pid;                // main process pid
 
48
 
 
49
int nthread;    // number of threads
 
50
int thread[32]; // thread pids
 
51
Map *map[32];   // thread maps
 
52
 
 
53
void
 
54
Usage(void)
 
55
{
 
56
        fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec]\n");
 
57
        fprint(2, "       prof [-t total_secs] [-d delta_msec] 6.out args ...\n");
 
58
        fprint(2, "\tformats (default -h):\n");
 
59
        fprint(2, "\t\t-P file.prof: write [c]pprof output to file.prof\n");
 
60
        fprint(2, "\t\t-h: histograms\n");
 
61
        fprint(2, "\t\t-f: dynamic functions\n");
 
62
        fprint(2, "\t\t-l: dynamic file and line numbers\n");
 
63
        fprint(2, "\t\t-r: dynamic registers\n");
 
64
        fprint(2, "\t\t-s: dynamic function stack traces\n");
 
65
        fprint(2, "\t\t-hs: include stack info in histograms\n");
 
66
        exit(2);
 
67
}
 
68
 
 
69
typedef struct PC PC;
 
70
struct PC {
 
71
        uvlong pc;
 
72
        uvlong callerpc;
 
73
        unsigned int count;
 
74
        PC* next;
 
75
};
 
76
 
 
77
enum {
 
78
        Ncounters = 256
 
79
};
 
80
 
 
81
PC *counters[Ncounters];
 
82
 
 
83
// Set up by setarch() to make most of the code architecture-independent.
 
84
typedef struct Arch Arch;
 
85
struct Arch {
 
86
        char*   name;
 
87
        void    (*regprint)(void);
 
88
        int     (*getregs)(Map*);
 
89
        int     (*getPC)(Map*);
 
90
        int     (*getSP)(Map*);
 
91
        uvlong  (*uregPC)(void);
 
92
        uvlong  (*uregSP)(void);
 
93
        void    (*ppword)(uvlong w);
 
94
};
 
95
 
 
96
void
 
97
amd64_regprint(void)
 
98
{
 
99
        fprint(2, "ax\t0x%llux\n", ureg_amd64.ax);
 
100
        fprint(2, "bx\t0x%llux\n", ureg_amd64.bx);
 
101
        fprint(2, "cx\t0x%llux\n", ureg_amd64.cx);
 
102
        fprint(2, "dx\t0x%llux\n", ureg_amd64.dx);
 
103
        fprint(2, "si\t0x%llux\n", ureg_amd64.si);
 
104
        fprint(2, "di\t0x%llux\n", ureg_amd64.di);
 
105
        fprint(2, "bp\t0x%llux\n", ureg_amd64.bp);
 
106
        fprint(2, "r8\t0x%llux\n", ureg_amd64.r8);
 
107
        fprint(2, "r9\t0x%llux\n", ureg_amd64.r9);
 
108
        fprint(2, "r10\t0x%llux\n", ureg_amd64.r10);
 
109
        fprint(2, "r11\t0x%llux\n", ureg_amd64.r11);
 
110
        fprint(2, "r12\t0x%llux\n", ureg_amd64.r12);
 
111
        fprint(2, "r13\t0x%llux\n", ureg_amd64.r13);
 
112
        fprint(2, "r14\t0x%llux\n", ureg_amd64.r14);
 
113
        fprint(2, "r15\t0x%llux\n", ureg_amd64.r15);
 
114
        fprint(2, "ds\t0x%llux\n", ureg_amd64.ds);
 
115
        fprint(2, "es\t0x%llux\n", ureg_amd64.es);
 
116
        fprint(2, "fs\t0x%llux\n", ureg_amd64.fs);
 
117
        fprint(2, "gs\t0x%llux\n", ureg_amd64.gs);
 
118
        fprint(2, "type\t0x%llux\n", ureg_amd64.type);
 
119
        fprint(2, "error\t0x%llux\n", ureg_amd64.error);
 
120
        fprint(2, "pc\t0x%llux\n", ureg_amd64.ip);
 
121
        fprint(2, "cs\t0x%llux\n", ureg_amd64.cs);
 
122
        fprint(2, "flags\t0x%llux\n", ureg_amd64.flags);
 
123
        fprint(2, "sp\t0x%llux\n", ureg_amd64.sp);
 
124
        fprint(2, "ss\t0x%llux\n", ureg_amd64.ss);
 
125
}
 
126
 
 
127
int
 
128
amd64_getregs(Map *map)
 
129
{
 
130
        int i;
 
131
        union {
 
132
                uvlong regs[1];
 
133
                struct Ureg_amd64 ureg;
 
134
        } u;
 
135
 
 
136
        for(i = 0; i < sizeof ureg_amd64; i+=8) {
 
137
                if(get8(map, (uvlong)i, &u.regs[i/8]) < 0)
 
138
                        return -1;
 
139
        }
 
140
        ureg_amd64 = u.ureg;
 
141
        return 0;
 
142
}
 
143
 
 
144
int
 
145
amd64_getPC(Map *map)
 
146
{
 
147
        uvlong x;
 
148
        int r;
 
149
 
 
150
        r = get8(map, offsetof(struct Ureg_amd64, ip), &x);
 
151
        ureg_amd64.ip = x;
 
152
        return r;
 
153
}
 
154
 
 
155
int
 
156
amd64_getSP(Map *map)
 
157
{
 
158
        uvlong x;
 
159
        int r;
 
160
 
 
161
        r = get8(map, offsetof(struct Ureg_amd64, sp), &x);
 
162
        ureg_amd64.sp = x;
 
163
        return r;
 
164
}
 
165
 
 
166
uvlong
 
167
amd64_uregPC(void)
 
168
{
 
169
        return ureg_amd64.ip;
 
170
}
 
171
 
 
172
uvlong
 
173
amd64_uregSP(void) {
 
174
        return ureg_amd64.sp;
 
175
}
 
176
 
 
177
void
 
178
amd64_ppword(uvlong w)
 
179
{
 
180
        uchar buf[8];
 
181
 
 
182
        buf[0] = w;
 
183
        buf[1] = w >> 8;
 
184
        buf[2] = w >> 16;
 
185
        buf[3] = w >> 24;
 
186
        buf[4] = w >> 32;
 
187
        buf[5] = w >> 40;
 
188
        buf[6] = w >> 48;
 
189
        buf[7] = w >> 56;
 
190
        Bwrite(pproffd, buf, 8);
 
191
}
 
192
 
 
193
void
 
194
x86_regprint(void)
 
195
{
 
196
        fprint(2, "ax\t0x%ux\n", ureg_x86.ax);
 
197
        fprint(2, "bx\t0x%ux\n", ureg_x86.bx);
 
198
        fprint(2, "cx\t0x%ux\n", ureg_x86.cx);
 
199
        fprint(2, "dx\t0x%ux\n", ureg_x86.dx);
 
200
        fprint(2, "si\t0x%ux\n", ureg_x86.si);
 
201
        fprint(2, "di\t0x%ux\n", ureg_x86.di);
 
202
        fprint(2, "bp\t0x%ux\n", ureg_x86.bp);
 
203
        fprint(2, "ds\t0x%ux\n", ureg_x86.ds);
 
204
        fprint(2, "es\t0x%ux\n", ureg_x86.es);
 
205
        fprint(2, "fs\t0x%ux\n", ureg_x86.fs);
 
206
        fprint(2, "gs\t0x%ux\n", ureg_x86.gs);
 
207
        fprint(2, "cs\t0x%ux\n", ureg_x86.cs);
 
208
        fprint(2, "flags\t0x%ux\n", ureg_x86.flags);
 
209
        fprint(2, "pc\t0x%ux\n", ureg_x86.pc);
 
210
        fprint(2, "sp\t0x%ux\n", ureg_x86.sp);
 
211
        fprint(2, "ss\t0x%ux\n", ureg_x86.ss);
 
212
}
 
213
 
 
214
int
 
215
x86_getregs(Map *map)
 
216
{
 
217
        int i;
 
218
 
 
219
        for(i = 0; i < sizeof ureg_x86; i+=4) {
 
220
                if(get4(map, (uvlong)i, &((uint32*)&ureg_x86)[i/4]) < 0)
 
221
                        return -1;
 
222
        }
 
223
        return 0;
 
224
}
 
225
 
 
226
int
 
227
x86_getPC(Map* map)
 
228
{
 
229
        return get4(map, offsetof(struct Ureg_x86, pc), &ureg_x86.pc);
 
230
}
 
231
 
 
232
int
 
233
x86_getSP(Map* map)
 
234
{
 
235
        return get4(map, offsetof(struct Ureg_x86, sp), &ureg_x86.sp);
 
236
}
 
237
 
 
238
uvlong
 
239
x86_uregPC(void)
 
240
{
 
241
        return (uvlong)ureg_x86.pc;
 
242
}
 
243
 
 
244
uvlong
 
245
x86_uregSP(void)
 
246
{
 
247
        return (uvlong)ureg_x86.sp;
 
248
}
 
249
 
 
250
void
 
251
x86_ppword(uvlong w)
 
252
{
 
253
        uchar buf[4];
 
254
 
 
255
        buf[0] = w;
 
256
        buf[1] = w >> 8;
 
257
        buf[2] = w >> 16;
 
258
        buf[3] = w >> 24;
 
259
        Bwrite(pproffd, buf, 4);
 
260
}
 
261
 
 
262
Arch archtab[] = {
 
263
        {
 
264
                "amd64",
 
265
                amd64_regprint,
 
266
                amd64_getregs,
 
267
                amd64_getPC,
 
268
                amd64_getSP,
 
269
                amd64_uregPC,
 
270
                amd64_uregSP,
 
271
                amd64_ppword,
 
272
        },
 
273
        {
 
274
                "386",
 
275
                x86_regprint,
 
276
                x86_getregs,
 
277
                x86_getPC,
 
278
                x86_getSP,
 
279
                x86_uregPC,
 
280
                x86_uregSP,
 
281
                x86_ppword,
 
282
        },
 
283
        {
 
284
                nil
 
285
        }
 
286
};
 
287
 
 
288
Arch *arch;
 
289
 
 
290
int
 
291
setarch(void)
 
292
{
 
293
        int i;
 
294
 
 
295
        if(mach != nil) {
 
296
                for(i = 0; archtab[i].name != nil; i++) {
 
297
                        if (strcmp(mach->name, archtab[i].name) == 0) {
 
298
                                arch = &archtab[i];
 
299
                                return 0;
 
300
                        }
 
301
                }
 
302
        }
 
303
        return -1;
 
304
}
 
305
 
 
306
int
 
307
getthreads(void)
 
308
{
 
309
        int i, j, curn, found;
 
310
        Map *curmap[nelem(map)];
 
311
        int curthread[nelem(map)];
 
312
        static int complained = 0;
 
313
 
 
314
        curn = procthreadpids(pid, curthread, nelem(curthread));
 
315
        if(curn <= 0)
 
316
                return curn;
 
317
 
 
318
        if(curn > nelem(map)) {
 
319
                if(complained == 0) {
 
320
                        fprint(2, "prof: too many threads; limiting to %d\n", nthread, nelem(map));
 
321
                        complained = 1;
 
322
                }
 
323
                curn = nelem(map);
 
324
        }
 
325
        if(curn == nthread && memcmp(thread, curthread, curn*sizeof(*thread)) == 0)
 
326
                return curn;    // no changes
 
327
 
 
328
        // Number of threads has changed (might be the init case).
 
329
        // A bit expensive but rare enough not to bother being clever.
 
330
        for(i = 0; i < curn; i++) {
 
331
                found = 0;
 
332
                for(j = 0; j < nthread; j++) {
 
333
                        if(curthread[i] == thread[j]) {
 
334
                                found = 1;
 
335
                                curmap[i] = map[j];
 
336
                                map[j] = nil;
 
337
                                break;
 
338
                        }
 
339
                }
 
340
                if(found)
 
341
                        continue;
 
342
 
 
343
                // map new thread
 
344
                curmap[i] = attachproc(curthread[i], &fhdr);
 
345
                if(curmap[i] == nil) {
 
346
                        fprint(2, "prof: can't attach to %d: %r\n", curthread[i]);
 
347
                        return -1;
 
348
                }
 
349
        }
 
350
 
 
351
        for(j = 0; j < nthread; j++)
 
352
                if(map[j] != nil)
 
353
                        detachproc(map[j]);
 
354
 
 
355
        nthread = curn;
 
356
        memmove(thread, curthread, nthread*sizeof thread[0]);
 
357
        memmove(map, curmap, sizeof map);
 
358
        return nthread;
 
359
}
 
360
 
 
361
int
 
362
sample(Map *map)
 
363
{
 
364
        static int n;
 
365
 
 
366
        n++;
 
367
        if(registers) {
 
368
                if(arch->getregs(map) < 0)
 
369
                        goto bad;
 
370
        } else {
 
371
                // we need only two registers
 
372
                if(arch->getPC(map) < 0)
 
373
                        goto bad;
 
374
                if(arch->getSP(map) < 0)
 
375
                        goto bad;
 
376
        }
 
377
        return 1;
 
378
bad:
 
379
        if(n == 1)
 
380
                fprint(2, "prof: can't read registers: %r\n");
 
381
        return 0;
 
382
}
 
383
 
 
384
void
 
385
addtohistogram(uvlong pc, uvlong callerpc, uvlong sp)
 
386
{
 
387
        int h;
 
388
        PC *x;
 
389
 
 
390
        h = (pc + callerpc*101) % Ncounters;
 
391
        for(x = counters[h]; x != NULL; x = x->next) {
 
392
                if(x->pc == pc && x->callerpc == callerpc) {
 
393
                        x->count++;
 
394
                        return;
 
395
                }
 
396
        }
 
397
        x = malloc(sizeof(PC));
 
398
        x->pc = pc;
 
399
        x->callerpc = callerpc;
 
400
        x->count = 1;
 
401
        x->next = counters[h];
 
402
        counters[h] = x;
 
403
}
 
404
 
 
405
void
 
406
addppword(uvlong pc)
 
407
{
 
408
        if(pc == 0) {
 
409
                return;
 
410
        }
 
411
        if(nppdata == ppalloc) {
 
412
                ppalloc = (1000+nppdata)*2;
 
413
                ppdata = realloc(ppdata, ppalloc * sizeof ppdata[0]);
 
414
                if(ppdata == nil) {
 
415
                        fprint(2, "prof: realloc failed: %r\n");
 
416
                        exit(2);
 
417
                }
 
418
        }
 
419
        ppdata[nppdata++] = pc;
 
420
}
 
421
 
 
422
void
 
423
startpptrace()
 
424
{
 
425
        ppstart = nppdata;
 
426
        addppword(~0);
 
427
}
 
428
 
 
429
void
 
430
endpptrace()
 
431
{
 
432
        ppdata[ppstart] = nppdata-ppstart-1;
 
433
}
 
434
 
 
435
uvlong nextpc;
 
436
 
 
437
void
 
438
xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym)
 
439
{
 
440
        char buf[1024];
 
441
        if(sym == nil){
 
442
                fprint(2, "syms\n");
 
443
                return;
 
444
        }
 
445
        if(histograms)
 
446
                addtohistogram(nextpc, pc, sp);
 
447
        if(!histograms || stacks > 1 || pprof) {
 
448
                if(nextpc == 0)
 
449
                        nextpc = sym->value;
 
450
                if(stacks){
 
451
                        fprint(2, "%s(", sym->name);
 
452
                        fprint(2, ")");
 
453
                        if(nextpc != sym->value)
 
454
                                fprint(2, "+%#llux ", nextpc - sym->value);
 
455
                        if(have_syms && linenums && fileline(buf, sizeof buf, pc)) {
 
456
                                fprint(2, " %s", buf);
 
457
                        }
 
458
                        fprint(2, "\n");
 
459
                }
 
460
                if (pprof) {
 
461
                        addppword(nextpc);
 
462
                }
 
463
        }
 
464
        nextpc = pc;
 
465
}
 
466
 
 
467
void
 
468
stacktracepcsp(Map *map, uvlong pc, uvlong sp)
 
469
{
 
470
        nextpc = pc;
 
471
        if(pprof){
 
472
                startpptrace();
 
473
        }
 
474
        if(machdata->ctrace==nil)
 
475
                fprint(2, "no machdata->ctrace\n");
 
476
        else if(machdata->ctrace(map, pc, sp, 0, xptrace) <= 0)
 
477
                fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp);
 
478
        else {
 
479
                addtohistogram(nextpc, 0, sp);
 
480
                if(stacks)
 
481
                        fprint(2, "\n");
 
482
        }
 
483
        if(pprof){
 
484
                endpptrace();
 
485
        }
 
486
}
 
487
 
 
488
void
 
489
printpc(Map *map, uvlong pc, uvlong sp)
 
490
{
 
491
        char buf[1024];
 
492
        if(registers)
 
493
                arch->regprint();
 
494
        if(have_syms > 0 && linenums &&  fileline(buf, sizeof buf, pc))
 
495
                fprint(2, "%s\n", buf);
 
496
        if(have_syms > 0 && functions) {
 
497
                symoff(buf, sizeof(buf), pc, CANY);
 
498
                fprint(2, "%s\n", buf);
 
499
        }
 
500
        if(stacks || pprof){
 
501
                stacktracepcsp(map, pc, sp);
 
502
        }
 
503
        else if(histograms){
 
504
                addtohistogram(pc, 0, sp);
 
505
        }
 
506
}
 
507
 
 
508
void
 
509
ppmaps(void)
 
510
{
 
511
        int fd, n;
 
512
        char tmp[100];
 
513
        Seg *seg;
 
514
 
 
515
        // If it's Linux, the info is in /proc/$pid/maps
 
516
        snprint(tmp, sizeof tmp, "/proc/%d/maps", pid);
 
517
        fd = open(tmp, 0);
 
518
        if(fd >= 0) {
 
519
                n = read(fd, ppmapdata, sizeof ppmapdata - 1);
 
520
                close(fd);
 
521
                if(n < 0) {
 
522
                        fprint(2, "prof: can't read %s: %r\n", tmp);
 
523
                        exit(2);
 
524
                }
 
525
                ppmapdata[n] = 0;
 
526
                return;
 
527
        }
 
528
 
 
529
        // It's probably a mac. Synthesize an entry for the text file.
 
530
        // The register segment may come first but it has a zero offset, so grab the first non-zero offset segment.
 
531
        for(n = 0; n < 3; n++){
 
532
                seg = &map[0]->seg[n];
 
533
                if(seg->b == 0) {
 
534
                        continue;
 
535
                }
 
536
                snprint(ppmapdata, sizeof ppmapdata,
 
537
                        "%.16x-%.16x r-xp %d 00:00 34968549                           %s\n",
 
538
                        seg->b, seg->e, seg->f, "/home/r/6.out"
 
539
                );
 
540
                return;
 
541
        }
 
542
        fprint(2, "prof: no text segment in maps for %s\n", file);
 
543
        exit(2);
 
544
}
 
545
 
 
546
void
 
547
samples(void)
 
548
{
 
549
        int i, pid, msec;
 
550
        struct timespec req;
 
551
        int getmaps;
 
552
 
 
553
        req.tv_sec = delta_msec/1000;
 
554
        req.tv_nsec = 1000000*(delta_msec % 1000);
 
555
        getmaps = 0;
 
556
        if(pprof)
 
557
                getmaps= 1;
 
558
        for(msec = 0; total_sec <= 0 || msec < 1000*total_sec; msec += delta_msec) {
 
559
                nsample++;
 
560
                nsamplethread += nthread;
 
561
                for(i = 0; i < nthread; i++) {
 
562
                        pid = thread[i];
 
563
                        if(ctlproc(pid, "stop") < 0)
 
564
                                return;
 
565
                        if(!sample(map[i])) {
 
566
                                ctlproc(pid, "start");
 
567
                                return;
 
568
                        }
 
569
                        printpc(map[i], arch->uregPC(), arch->uregSP());
 
570
                        ctlproc(pid, "start");
 
571
                }
 
572
                nanosleep(&req, NULL);
 
573
                getthreads();
 
574
                if(nthread == 0)
 
575
                        break;
 
576
                if(getmaps) {
 
577
                        getmaps = 0;
 
578
                        ppmaps();
 
579
                }
 
580
        }
 
581
}
 
582
 
 
583
typedef struct Func Func;
 
584
struct Func
 
585
{
 
586
        Func *next;
 
587
        Symbol s;
 
588
        uint onstack;
 
589
        uint leaf;
 
590
};
 
591
 
 
592
Func *func[257];
 
593
int nfunc;
 
594
 
 
595
Func*
 
596
findfunc(uvlong pc)
 
597
{
 
598
        Func *f;
 
599
        uint h;
 
600
        Symbol s;
 
601
 
 
602
        if(pc == 0)
 
603
                return nil;
 
604
 
 
605
        if(!findsym(pc, CTEXT, &s))
 
606
                return nil;
 
607
 
 
608
        h = s.value % nelem(func);
 
609
        for(f = func[h]; f != NULL; f = f->next)
 
610
                if(f->s.value == s.value)
 
611
                        return f;
 
612
 
 
613
        f = malloc(sizeof *f);
 
614
        memset(f, 0, sizeof *f);
 
615
        f->s = s;
 
616
        f->next = func[h];
 
617
        func[h] = f;
 
618
        nfunc++;
 
619
        return f;
 
620
}
 
621
 
 
622
int
 
623
compareleaf(const void *va, const void *vb)
 
624
{
 
625
        Func *a, *b;
 
626
 
 
627
        a = *(Func**)va;
 
628
        b = *(Func**)vb;
 
629
        if(a->leaf != b->leaf)
 
630
                return b->leaf - a->leaf;
 
631
        if(a->onstack != b->onstack)
 
632
                return b->onstack - a->onstack;
 
633
        return strcmp(a->s.name, b->s.name);
 
634
}
 
635
 
 
636
void
 
637
dumphistogram()
 
638
{
 
639
        int i, h, n;
 
640
        PC *x;
 
641
        Func *f, **ff;
 
642
 
 
643
        if(!histograms)
 
644
                return;
 
645
 
 
646
        // assign counts to functions.
 
647
        for(h = 0; h < Ncounters; h++) {
 
648
                for(x = counters[h]; x != NULL; x = x->next) {
 
649
                        f = findfunc(x->pc);
 
650
                        if(f) {
 
651
                                f->onstack += x->count;
 
652
                                f->leaf += x->count;
 
653
                        }
 
654
                        f = findfunc(x->callerpc);
 
655
                        if(f)
 
656
                                f->leaf -= x->count;
 
657
                }
 
658
        }
 
659
 
 
660
        // build array
 
661
        ff = malloc(nfunc*sizeof ff[0]);
 
662
        n = 0;
 
663
        for(h = 0; h < nelem(func); h++)
 
664
                for(f = func[h]; f != NULL; f = f->next)
 
665
                        ff[n++] = f;
 
666
 
 
667
        // sort by leaf counts
 
668
        qsort(ff, nfunc, sizeof ff[0], compareleaf);
 
669
 
 
670
        // print.
 
671
        fprint(2, "%d samples (avg %.1g threads)\n", nsample, (double)nsamplethread/nsample);
 
672
        for(i = 0; i < nfunc; i++) {
 
673
                f = ff[i];
 
674
                fprint(2, "%6.2f%%\t", 100.0*(double)f->leaf/nsample);
 
675
                if(stacks)
 
676
                        fprint(2, "%6.2f%%\t", 100.0*(double)f->onstack/nsample);
 
677
                fprint(2, "%s\n", f->s.name);
 
678
        }
 
679
}
 
680
 
 
681
typedef struct Trace Trace;
 
682
struct Trace {
 
683
        int     count;
 
684
        int     npc;
 
685
        uvlong  *pc;
 
686
        Trace   *next;
 
687
};
 
688
 
 
689
void
 
690
dumppprof()
 
691
{
 
692
        uvlong i, n, *p, *e;
 
693
        int ntrace;
 
694
        Trace *trace, *tp, *up, *prev;
 
695
 
 
696
        if(!pprof)
 
697
                return;
 
698
        e = ppdata + nppdata;
 
699
        // Create list of traces.  First, count the traces
 
700
        ntrace = 0;
 
701
        for(p = ppdata; p < e;) {
 
702
                n = *p++;
 
703
                p += n;
 
704
                if(n == 0)
 
705
                        continue;
 
706
                ntrace++;
 
707
        }
 
708
        if(ntrace <= 0)
 
709
                return;
 
710
        // Allocate and link the traces together.
 
711
        trace = malloc(ntrace * sizeof(Trace));
 
712
        tp = trace;
 
713
        for(p = ppdata; p < e;) {
 
714
                n = *p++;
 
715
                if(n == 0)
 
716
                        continue;
 
717
                tp->count = 1;
 
718
                tp->npc = n;
 
719
                tp->pc = p;
 
720
                tp->next = tp+1;
 
721
                tp++;
 
722
                p += n;
 
723
        }
 
724
        trace[ntrace-1].next = nil;
 
725
        // Eliminate duplicates.  Lousy algorithm, although not as bad as it looks because
 
726
        // the list collapses fast.
 
727
        for(tp = trace; tp != nil; tp = tp->next) {
 
728
                prev = tp;
 
729
                for(up = tp->next; up != nil; up = up->next) {
 
730
                        if(up->npc == tp->npc && memcmp(up->pc, tp->pc, up->npc*sizeof up->pc[0]) == 0) {
 
731
                                tp->count++;
 
732
                                prev->next = up->next;
 
733
                        } else {
 
734
                                prev = up;
 
735
                        }
 
736
                }
 
737
        }
 
738
        // Write file.
 
739
        // See http://code.google.com/p/google-perftools/source/browse/trunk/doc/cpuprofile-fileformat.html
 
740
        // 1) Header
 
741
        arch->ppword(0);        // must be zero
 
742
        arch->ppword(3);        // 3 words follow in header
 
743
        arch->ppword(0);        // must be zero
 
744
        arch->ppword(delta_msec * 1000);        // sampling period in microseconds
 
745
        arch->ppword(0);        // must be zero (padding)
 
746
        // 2) One record for each trace.
 
747
        for(tp = trace; tp != nil; tp = tp->next) {
 
748
                arch->ppword(tp->count);
 
749
                arch->ppword(tp->npc);
 
750
                for(i = 0; i < tp->npc; i++) {
 
751
                        arch->ppword(tp->pc[i]);
 
752
                }
 
753
        }
 
754
        // 3) Binary trailer
 
755
        arch->ppword(0);        // must be zero
 
756
        arch->ppword(1);        // must be one
 
757
        arch->ppword(0);        // must be zero
 
758
        // 4) Mapped objects.
 
759
        Bwrite(pproffd, ppmapdata, strlen(ppmapdata));
 
760
        // 5) That's it.
 
761
        Bterm(pproffd);
 
762
}
 
763
 
 
764
int
 
765
startprocess(char **argv)
 
766
{
 
767
        int pid;
 
768
 
 
769
        if((pid = fork()) == 0) {
 
770
                pid = getpid();
 
771
                if(ctlproc(pid, "hang") < 0){
 
772
                        fprint(2, "prof: child process could not hang\n");
 
773
                        exits(0);
 
774
                }
 
775
                execv(argv[0], argv);
 
776
                fprint(2, "prof: could not exec %s: %r\n", argv[0]);
 
777
                exits(0);
 
778
        }
 
779
 
 
780
        if(pid == -1) {
 
781
                fprint(2, "prof: could not fork\n");
 
782
                exit(1);
 
783
        }
 
784
        if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0) {
 
785
                fprint(2, "prof: could not attach to child process: %r\n");
 
786
                exit(1);
 
787
        }
 
788
        return pid;
 
789
}
 
790
 
 
791
void
 
792
detach(void)
 
793
{
 
794
        int i;
 
795
 
 
796
        for(i = 0; i < nthread; i++)
 
797
                detachproc(map[i]);
 
798
}
 
799
 
 
800
int
 
801
main(int argc, char *argv[])
 
802
{
 
803
        int i;
 
804
        char *ppfile;
 
805
 
 
806
        ARGBEGIN{
 
807
        case 'P':
 
808
                pprof =1;
 
809
                ppfile = EARGF(Usage());
 
810
                pproffd = Bopen(ppfile, OWRITE);
 
811
                if(pproffd == nil) {
 
812
                        fprint(2, "prof: cannot open %s: %r\n", ppfile);
 
813
                        exit(2);
 
814
                }
 
815
                break;
 
816
        case 'd':
 
817
                delta_msec = atoi(EARGF(Usage()));
 
818
                break;
 
819
        case 't':
 
820
                total_sec = atoi(EARGF(Usage()));
 
821
                break;
 
822
        case 'p':
 
823
                pid = atoi(EARGF(Usage()));
 
824
                break;
 
825
        case 'f':
 
826
                functions = 1;
 
827
                break;
 
828
        case 'h':
 
829
                histograms = 1;
 
830
                break;
 
831
        case 'l':
 
832
                linenums = 1;
 
833
                break;
 
834
        case 'r':
 
835
                registers = 1;
 
836
                break;
 
837
        case 's':
 
838
                stacks++;
 
839
                break;
 
840
        default:
 
841
                Usage();
 
842
        }ARGEND
 
843
        if(pid <= 0 && argc == 0)
 
844
                Usage();
 
845
        if(functions+linenums+registers+stacks+pprof == 0)
 
846
                histograms = 1;
 
847
        if(!machbyname("amd64")) {
 
848
                fprint(2, "prof: no amd64 support\n", pid);
 
849
                exit(1);
 
850
        }
 
851
        if(argc > 0)
 
852
                file = argv[0];
 
853
        else if(pid) {
 
854
                file = proctextfile(pid);
 
855
                if (file == NULL) {
 
856
                        fprint(2, "prof: can't find file for pid %d: %r\n", pid);
 
857
                        fprint(2, "prof: on Darwin, need to provide file name explicitly\n");
 
858
                        exit(1);
 
859
                }
 
860
        }
 
861
        fd = open(file, 0);
 
862
        if(fd < 0) {
 
863
                fprint(2, "prof: can't open %s: %r\n", file);
 
864
                exit(1);
 
865
        }
 
866
        if(crackhdr(fd, &fhdr)) {
 
867
                have_syms = syminit(fd, &fhdr);
 
868
                if(!have_syms) {
 
869
                        fprint(2, "prof: no symbols for %s: %r\n", file);
 
870
                }
 
871
        } else {
 
872
                fprint(2, "prof: crack header for %s: %r\n", file);
 
873
                exit(1);
 
874
        }
 
875
        if(pid <= 0)
 
876
                pid = startprocess(argv);
 
877
        attachproc(pid, &fhdr); // initializes thread list
 
878
        if(setarch() < 0) {
 
879
                detach();
 
880
                fprint(2, "prof: can't identify binary architecture for pid %d\n", pid);
 
881
                exit(1);
 
882
        }
 
883
        if(getthreads() <= 0) {
 
884
                detach();
 
885
                fprint(2, "prof: can't find threads for pid %d\n", pid);
 
886
                exit(1);
 
887
        }
 
888
        for(i = 0; i < nthread; i++)
 
889
                ctlproc(thread[i], "start");
 
890
        samples();
 
891
        detach();
 
892
        dumphistogram();
 
893
        dumppprof();
 
894
        exit(0);
 
895
}