LEFT | RIGHT |
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // Malloc profiling. | 5 // Malloc profiling. |
6 // Patterned after tcmalloc's algorithms; shorter code. | 6 // Patterned after tcmalloc's algorithms; shorter code. |
7 | 7 |
8 package runtime | 8 package runtime |
9 #include "runtime.h" | 9 #include "runtime.h" |
10 #include "arch_GOARCH.h" | 10 #include "arch_GOARCH.h" |
11 #include "malloc.h" | 11 #include "malloc.h" |
12 #include "defs_GOOS_GOARCH.h" | 12 #include "defs_GOOS_GOARCH.h" |
13 #include "type.h" | 13 #include "type.h" |
14 | 14 |
15 // NOTE(rsc): Everything here could use cas if contention became an issue. | 15 // NOTE(rsc): Everything here could use cas if contention became an issue. |
16 static Lock proflock; | 16 static Lock proflock; |
17 | 17 |
18 enum { MProf, CProf }; // profile types | 18 enum { MProf, BProf }; // profile types |
19 | 19 |
20 // Per-call-stack profiling information. | 20 // Per-call-stack profiling information. |
21 // Lookup by hashing call stack into a linked-list hash table. | 21 // Lookup by hashing call stack into a linked-list hash table. |
22 typedef struct Bucket Bucket; | 22 typedef struct Bucket Bucket; |
23 struct Bucket | 23 struct Bucket |
24 { | 24 { |
25 Bucket *next; // next in hash list | 25 Bucket *next; // next in hash list |
26 » Bucket» *allnext;» // next in list of all mbuckets/cbuckets | 26 » Bucket» *allnext;» // next in list of all mbuckets/bbuckets |
27 int32 typ; | 27 int32 typ; |
28 union | 28 union |
29 { | 29 { |
30 struct // typ == MProf | 30 struct // typ == MProf |
31 { | 31 { |
32 uintptr allocs; | 32 uintptr allocs; |
33 uintptr frees; | 33 uintptr frees; |
34 uintptr alloc_bytes; | 34 uintptr alloc_bytes; |
35 uintptr free_bytes; | 35 uintptr free_bytes; |
36 uintptr recent_allocs; // since last gc | 36 uintptr recent_allocs; // since last gc |
37 uintptr recent_frees; | 37 uintptr recent_frees; |
38 uintptr recent_alloc_bytes; | 38 uintptr recent_alloc_bytes; |
39 uintptr recent_free_bytes; | 39 uintptr recent_free_bytes; |
40 }; | 40 }; |
41 » » struct // typ == CProf | 41 » » struct // typ == BProf |
42 { | 42 { |
43 int64 count; | 43 int64 count; |
44 int64 cycles; | 44 int64 cycles; |
45 }; | 45 }; |
46 }; | 46 }; |
47 uintptr hash; | 47 uintptr hash; |
48 uintptr nstk; | 48 uintptr nstk; |
49 uintptr stk[1]; | 49 uintptr stk[1]; |
50 }; | 50 }; |
51 enum { | 51 enum { |
52 BuckHashSize = 179999, | 52 BuckHashSize = 179999, |
53 }; | 53 }; |
54 static Bucket **buckhash; | 54 static Bucket **buckhash; |
55 static Bucket *mbuckets; | 55 static Bucket *mbuckets; // memory profile buckets |
56 static Bucket *cbuckets; | 56 static Bucket *bbuckets; // blocking profile buckets |
57 static uintptr bucketmem; | 57 static uintptr bucketmem; |
58 | 58 |
59 // Return the bucket for stk[0:nstk], allocating new bucket if needed. | 59 // Return the bucket for stk[0:nstk], allocating new bucket if needed. |
60 static Bucket* | 60 static Bucket* |
61 stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc) | 61 stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc) |
62 { | 62 { |
63 int32 i; | 63 int32 i; |
64 uintptr h; | 64 uintptr h; |
65 Bucket *b; | 65 Bucket *b; |
66 | 66 |
(...skipping 26 matching lines...) Expand all Loading... |
93 runtime·memmove(b->stk, stk, nstk*sizeof stk[0]); | 93 runtime·memmove(b->stk, stk, nstk*sizeof stk[0]); |
94 b->typ = typ; | 94 b->typ = typ; |
95 b->hash = h; | 95 b->hash = h; |
96 b->nstk = nstk; | 96 b->nstk = nstk; |
97 b->next = buckhash[i]; | 97 b->next = buckhash[i]; |
98 buckhash[i] = b; | 98 buckhash[i] = b; |
99 if(typ == MProf) { | 99 if(typ == MProf) { |
100 b->allnext = mbuckets; | 100 b->allnext = mbuckets; |
101 mbuckets = b; | 101 mbuckets = b; |
102 } else { | 102 } else { |
103 » » b->allnext = cbuckets; | 103 » » b->allnext = bbuckets; |
104 » » cbuckets = b; | 104 » » bbuckets = b; |
105 } | 105 } |
106 return b; | 106 return b; |
107 } | 107 } |
108 | 108 |
109 // Record that a gc just happened: all the 'recent' statistics are now real. | 109 // Record that a gc just happened: all the 'recent' statistics are now real. |
110 void | 110 void |
111 runtime·MProf_GC(void) | 111 runtime·MProf_GC(void) |
112 { | 112 { |
113 Bucket *b; | 113 Bucket *b; |
114 ········ | 114 ········ |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
270 runtime·lock(&proflock); | 270 runtime·lock(&proflock); |
271 b = getaddrbucket((uintptr)p); | 271 b = getaddrbucket((uintptr)p); |
272 if(b != nil) { | 272 if(b != nil) { |
273 b->recent_frees++; | 273 b->recent_frees++; |
274 b->recent_free_bytes += size; | 274 b->recent_free_bytes += size; |
275 } | 275 } |
276 runtime·unlock(&proflock); | 276 runtime·unlock(&proflock); |
277 m->nomemprof--; | 277 m->nomemprof--; |
278 } | 278 } |
279 | 279 |
| 280 int64 runtime·blockprofilerate; // in CPU ticks |
| 281 |
280 void | 282 void |
281 runtime·contentionevent(int64 cycles, int32 skip) | 283 runtime·SetBlockProfileRate(intgo rate) |
282 { | 284 { |
283 » int32 nstk, rate; | 285 » runtime·atomicstore64((uint64*)&runtime·blockprofilerate, rate * runtime
·tickspersecond() / (1000*1000*1000)); |
| 286 } |
| 287 |
| 288 void |
| 289 runtime·blockevent(int64 cycles, int32 skip) |
| 290 { |
| 291 » int32 nstk; |
| 292 » int64 rate; |
284 uintptr stk[32]; | 293 uintptr stk[32]; |
285 Bucket *b; | 294 Bucket *b; |
286 | 295 |
287 » rate = runtime·ContentionProfileRate; | 296 » if(cycles <= 0) |
288 » if(rate <= 0 || cycles <= 0) | |
289 return; | 297 return; |
290 » if(rate > cycles && runtime·fastrand1()%rate > cycles) | 298 » rate = runtime·atomicload64((uint64*)&runtime·blockprofilerate); |
| 299 » if(rate <= 0 || (rate > cycles && runtime·fastrand1()%rate > cycles)) |
291 return; | 300 return; |
292 | 301 |
293 nstk = runtime·callers(skip, stk, 32); | 302 nstk = runtime·callers(skip, stk, 32); |
294 runtime·lock(&proflock); | 303 runtime·lock(&proflock); |
295 » b = stkbucket(CProf, stk, nstk, true); | 304 » b = stkbucket(BProf, stk, nstk, true); |
296 b->count++; | 305 b->count++; |
297 b->cycles += cycles; | 306 b->cycles += cycles; |
298 runtime·unlock(&proflock); | 307 runtime·unlock(&proflock); |
299 } | 308 } |
300 | 309 |
301 // Go interface to profile data. (Declared in extern.go) | 310 // Go interface to profile data. (Declared in extern.go) |
302 // Assumes Go sizeof(int) == sizeof(int32) | 311 // Assumes Go sizeof(int) == sizeof(int32) |
303 | 312 |
304 // Must match MemProfileRecord in debug.go. | 313 // Must match MemProfileRecord in debug.go. |
305 typedef struct Record Record; | 314 typedef struct Record Record; |
(...skipping 12 matching lines...) Expand all Loading... |
318 r->alloc_bytes = b->alloc_bytes; | 327 r->alloc_bytes = b->alloc_bytes; |
319 r->free_bytes = b->free_bytes; | 328 r->free_bytes = b->free_bytes; |
320 r->alloc_objects = b->allocs; | 329 r->alloc_objects = b->allocs; |
321 r->free_objects = b->frees; | 330 r->free_objects = b->frees; |
322 for(i=0; i<b->nstk && i<nelem(r->stk); i++) | 331 for(i=0; i<b->nstk && i<nelem(r->stk); i++) |
323 r->stk[i] = b->stk[i]; | 332 r->stk[i] = b->stk[i]; |
324 for(; i<nelem(r->stk); i++) | 333 for(; i<nelem(r->stk); i++) |
325 r->stk[i] = 0; | 334 r->stk[i] = 0; |
326 } | 335 } |
327 | 336 |
328 func MemProfile(p Slice, include_inuse_zero bool) (n int32, ok bool) { | 337 func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) { |
329 Bucket *b; | 338 Bucket *b; |
330 Record *r; | 339 Record *r; |
331 | 340 |
332 runtime·lock(&proflock); | 341 runtime·lock(&proflock); |
333 n = 0; | 342 n = 0; |
334 for(b=mbuckets; b; b=b->allnext) | 343 for(b=mbuckets; b; b=b->allnext) |
335 if(include_inuse_zero || b->alloc_bytes != b->free_bytes) | 344 if(include_inuse_zero || b->alloc_bytes != b->free_bytes) |
336 n++; | 345 n++; |
337 ok = false; | 346 ok = false; |
338 if(n <= p.len) { | 347 if(n <= p.len) { |
339 ok = true; | 348 ok = true; |
340 r = (Record*)p.array; | 349 r = (Record*)p.array; |
341 for(b=mbuckets; b; b=b->allnext) | 350 for(b=mbuckets; b; b=b->allnext) |
342 if(include_inuse_zero || b->alloc_bytes != b->free_bytes
) | 351 if(include_inuse_zero || b->alloc_bytes != b->free_bytes
) |
343 record(r++, b); | 352 record(r++, b); |
344 } | 353 } |
345 runtime·unlock(&proflock); | 354 runtime·unlock(&proflock); |
346 } | 355 } |
347 | 356 |
348 // Must match ContentionProfileRecord in debug.go. | 357 // Must match BlockProfileRecord in debug.go. |
349 typedef struct CRecord CRecord; | 358 typedef struct BRecord BRecord; |
350 struct CRecord { | 359 struct BRecord { |
351 int64 count; | 360 int64 count; |
352 int64 cycles; | 361 int64 cycles; |
353 uintptr stk[32]; | 362 uintptr stk[32]; |
354 }; | 363 }; |
355 | 364 |
356 func ContentionProfile(p Slice) (n int32, ok bool) { | 365 func BlockProfile(p Slice) (n int, ok bool) { |
357 » Bucket *b; | 366 » Bucket *b; |
358 » CRecord *r; | 367 » BRecord *r; |
359 int32 i; | 368 int32 i; |
360 | 369 |
361 runtime·lock(&proflock); | 370 runtime·lock(&proflock); |
362 n = 0; | 371 n = 0; |
363 » for(b=cbuckets; b; b=b->allnext) | 372 » for(b=bbuckets; b; b=b->allnext) |
364 n++; | 373 n++; |
365 ok = false; | 374 ok = false; |
366 if(n <= p.len) { | 375 if(n <= p.len) { |
367 ok = true; | 376 ok = true; |
368 » » r = (CRecord*)p.array; | 377 » » r = (BRecord*)p.array; |
369 » » for(b=cbuckets; b; b=b->allnext, r++) { | 378 » » for(b=bbuckets; b; b=b->allnext, r++) { |
370 r->count = b->count; | 379 r->count = b->count; |
371 r->cycles = b->cycles; | 380 r->cycles = b->cycles; |
372 for(i=0; i<b->nstk && i<nelem(r->stk); i++) | 381 for(i=0; i<b->nstk && i<nelem(r->stk); i++) |
373 r->stk[i] = b->stk[i]; | 382 r->stk[i] = b->stk[i]; |
374 for(; i<nelem(r->stk); i++) | 383 for(; i<nelem(r->stk); i++) |
375 r->stk[i] = 0;·················· | 384 r->stk[i] = 0;·················· |
376 } | 385 } |
377 } | 386 } |
378 runtime·unlock(&proflock); | 387 runtime·unlock(&proflock); |
379 } | 388 } |
380 | 389 |
381 // Must match StackRecord in debug.go. | 390 // Must match StackRecord in debug.go. |
382 typedef struct TRecord TRecord; | 391 typedef struct TRecord TRecord; |
383 struct TRecord { | 392 struct TRecord { |
384 uintptr stk[32]; | 393 uintptr stk[32]; |
385 }; | 394 }; |
386 | 395 |
387 func ThreadCreateProfile(p Slice) (n int32, ok bool) { | 396 func ThreadCreateProfile(p Slice) (n int, ok bool) { |
388 TRecord *r; | 397 TRecord *r; |
389 M *first, *m; | 398 M *first, *m; |
390 ········ | 399 ········ |
391 first = runtime·atomicloadp(&runtime·allm); | 400 first = runtime·atomicloadp(&runtime·allm); |
392 n = 0; | 401 n = 0; |
393 for(m=first; m; m=m->alllink) | 402 for(m=first; m; m=m->alllink) |
394 n++; | 403 n++; |
395 ok = false; | 404 ok = false; |
396 if(n <= p.len) { | 405 if(n <= p.len) { |
397 ok = true; | 406 ok = true; |
398 r = (TRecord*)p.array; | 407 r = (TRecord*)p.array; |
399 for(m=first; m; m=m->alllink) { | 408 for(m=first; m; m=m->alllink) { |
400 runtime·memmove(r->stk, m->createstack, sizeof r->stk); | 409 runtime·memmove(r->stk, m->createstack, sizeof r->stk); |
401 r++; | 410 r++; |
402 } | 411 } |
403 } | 412 } |
404 } | 413 } |
405 | 414 |
406 func Stack(b Slice, all bool) (n int32) { | 415 func Stack(b Slice, all bool) (n int) { |
407 byte *pc, *sp; | 416 byte *pc, *sp; |
408 ········ | 417 ········ |
409 sp = runtime·getcallersp(&b); | 418 sp = runtime·getcallersp(&b); |
410 pc = runtime·getcallerpc(&b); | 419 pc = runtime·getcallerpc(&b); |
411 | 420 |
412 if(all) { | 421 if(all) { |
413 runtime·semacquire(&runtime·worldsema); | 422 runtime·semacquire(&runtime·worldsema); |
414 m->gcing = 1; | 423 m->gcing = 1; |
415 runtime·stoptheworld(); | 424 runtime·stoptheworld(); |
416 } | 425 } |
(...skipping 22 matching lines...) Expand all Loading... |
439 static void | 448 static void |
440 saveg(byte *pc, byte *sp, G *g, TRecord *r) | 449 saveg(byte *pc, byte *sp, G *g, TRecord *r) |
441 { | 450 { |
442 int32 n; | 451 int32 n; |
443 ········ | 452 ········ |
444 n = runtime·gentraceback(pc, sp, 0, g, 0, r->stk, nelem(r->stk)); | 453 n = runtime·gentraceback(pc, sp, 0, g, 0, r->stk, nelem(r->stk)); |
445 if(n < nelem(r->stk)) | 454 if(n < nelem(r->stk)) |
446 r->stk[n] = 0; | 455 r->stk[n] = 0; |
447 } | 456 } |
448 | 457 |
449 func GoroutineProfile(b Slice) (n int32, ok bool) { | 458 func GoroutineProfile(b Slice) (n int, ok bool) { |
450 byte *pc, *sp; | 459 byte *pc, *sp; |
451 TRecord *r; | 460 TRecord *r; |
452 G *gp; | 461 G *gp; |
453 ········ | 462 ········ |
454 sp = runtime·getcallersp(&b); | 463 sp = runtime·getcallersp(&b); |
455 pc = runtime·getcallerpc(&b); | 464 pc = runtime·getcallerpc(&b); |
456 ········ | 465 ········ |
457 ok = false; | 466 ok = false; |
458 n = runtime·gcount(); | 467 n = runtime·gcount(); |
459 if(n <= b.len) { | 468 if(n <= b.len) { |
(...skipping 12 matching lines...) Expand all Loading... |
472 saveg(gp->sched.pc, (byte*)gp->sched.sp, gp, r++
); | 481 saveg(gp->sched.pc, (byte*)gp->sched.sp, gp, r++
); |
473 } | 482 } |
474 } | 483 } |
475 ········ | 484 ········ |
476 m->gcing = 0; | 485 m->gcing = 0; |
477 runtime·semrelease(&runtime·worldsema); | 486 runtime·semrelease(&runtime·worldsema); |
478 runtime·starttheworld(); | 487 runtime·starttheworld(); |
479 } | 488 } |
480 } | 489 } |
481 | 490 |
LEFT | RIGHT |