LEFT | RIGHT |
(no file at all) | |
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 // Per-call-stack allocation information. | 18 enum { MProf, BProf }; // profile types |
| 19 |
| 20 // Per-call-stack profiling information. |
19 // Lookup by hashing call stack into a linked-list hash table. | 21 // Lookup by hashing call stack into a linked-list hash table. |
20 typedef struct Bucket Bucket; | 22 typedef struct Bucket Bucket; |
21 struct Bucket | 23 struct Bucket |
22 { | 24 { |
23 Bucket *next; // next in hash list | 25 Bucket *next; // next in hash list |
24 » Bucket» *allnext;» // next in list of all buckets | 26 » Bucket» *allnext;» // next in list of all mbuckets/bbuckets |
25 » uintptr»allocs; | 27 » int32» typ; |
26 » uintptr»frees; | 28 » union |
27 » uintptr»alloc_bytes; | 29 » { |
28 » uintptr»free_bytes; | 30 » » struct // typ == MProf |
29 » uintptr»recent_allocs; // since last gc | 31 » » { |
30 » uintptr»recent_frees; | 32 » » » uintptr»allocs; |
31 » uintptr»recent_alloc_bytes; | 33 » » » uintptr»frees; |
32 » uintptr»recent_free_bytes; | 34 » » » uintptr»alloc_bytes; |
| 35 » » » uintptr»free_bytes; |
| 36 » » » uintptr»recent_allocs; // since last gc |
| 37 » » » uintptr»recent_frees; |
| 38 » » » uintptr»recent_alloc_bytes; |
| 39 » » » uintptr»recent_free_bytes; |
| 40 » » }; |
| 41 » » struct // typ == BProf |
| 42 » » { |
| 43 » » » int64» count; |
| 44 » » » int64» cycles; |
| 45 » » }; |
| 46 » }; |
33 uintptr hash; | 47 uintptr hash; |
34 uintptr nstk; | 48 uintptr nstk; |
35 uintptr stk[1]; | 49 uintptr stk[1]; |
36 }; | 50 }; |
37 enum { | 51 enum { |
38 BuckHashSize = 179999, | 52 BuckHashSize = 179999, |
39 }; | 53 }; |
40 static Bucket **buckhash; | 54 static Bucket **buckhash; |
41 static Bucket *buckets; | 55 static Bucket *mbuckets; // memory profile buckets |
| 56 static Bucket *bbuckets; // blocking profile buckets |
42 static uintptr bucketmem; | 57 static uintptr bucketmem; |
43 | 58 |
44 // 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. |
45 static Bucket* | 60 static Bucket* |
46 stkbucket(uintptr *stk, int32 nstk, bool alloc) | 61 stkbucket(int32 typ, uintptr *stk, int32 nstk, bool alloc) |
47 { | 62 { |
48 int32 i; | 63 int32 i; |
49 uintptr h; | 64 uintptr h; |
50 Bucket *b; | 65 Bucket *b; |
51 | 66 |
52 if(buckhash == nil) { | 67 if(buckhash == nil) { |
53 buckhash = runtime·SysAlloc(BuckHashSize*sizeof buckhash[0]); | 68 buckhash = runtime·SysAlloc(BuckHashSize*sizeof buckhash[0]); |
54 mstats.buckhash_sys += BuckHashSize*sizeof buckhash[0]; | 69 mstats.buckhash_sys += BuckHashSize*sizeof buckhash[0]; |
55 } | 70 } |
56 | 71 |
57 // Hash stack. | 72 // Hash stack. |
58 h = 0; | 73 h = 0; |
59 for(i=0; i<nstk; i++) { | 74 for(i=0; i<nstk; i++) { |
60 h += stk[i]; | 75 h += stk[i]; |
61 h += h<<10; | 76 h += h<<10; |
62 h ^= h>>6; | 77 h ^= h>>6; |
63 } | 78 } |
64 h += h<<3; | 79 h += h<<3; |
65 h ^= h>>11; | 80 h ^= h>>11; |
66 | 81 |
67 i = h%BuckHashSize; | 82 i = h%BuckHashSize; |
68 for(b = buckhash[i]; b; b=b->next) | 83 for(b = buckhash[i]; b; b=b->next) |
69 » » if(b->hash == h && b->nstk == nstk && | 84 » » if(b->typ == typ && b->hash == h && b->nstk == nstk && |
70 runtime·mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) =
= 0) | 85 runtime·mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) =
= 0) |
71 return b; | 86 return b; |
72 | 87 |
73 if(!alloc) | 88 if(!alloc) |
74 return nil; | 89 return nil; |
75 | 90 |
76 b = runtime·mallocgc(sizeof *b + nstk*sizeof stk[0], FlagNoProfiling, 0,
1); | 91 b = runtime·mallocgc(sizeof *b + nstk*sizeof stk[0], FlagNoProfiling, 0,
1); |
77 bucketmem += sizeof *b + nstk*sizeof stk[0]; | 92 bucketmem += sizeof *b + nstk*sizeof stk[0]; |
78 runtime·memmove(b->stk, stk, nstk*sizeof stk[0]); | 93 runtime·memmove(b->stk, stk, nstk*sizeof stk[0]); |
| 94 b->typ = typ; |
79 b->hash = h; | 95 b->hash = h; |
80 b->nstk = nstk; | 96 b->nstk = nstk; |
81 b->next = buckhash[i]; | 97 b->next = buckhash[i]; |
82 buckhash[i] = b; | 98 buckhash[i] = b; |
83 » b->allnext = buckets; | 99 » if(typ == MProf) { |
84 » buckets = b; | 100 » » b->allnext = mbuckets; |
| 101 » » mbuckets = b; |
| 102 » } else { |
| 103 » » b->allnext = bbuckets; |
| 104 » » bbuckets = b; |
| 105 » } |
85 return b; | 106 return b; |
86 } | 107 } |
87 | 108 |
88 // 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. |
89 void | 110 void |
90 runtime·MProf_GC(void) | 111 runtime·MProf_GC(void) |
91 { | 112 { |
92 Bucket *b; | 113 Bucket *b; |
93 ········ | 114 ········ |
94 runtime·lock(&proflock); | 115 runtime·lock(&proflock); |
95 » for(b=buckets; b; b=b->allnext) { | 116 » for(b=mbuckets; b; b=b->allnext) { |
96 b->allocs += b->recent_allocs; | 117 b->allocs += b->recent_allocs; |
97 b->frees += b->recent_frees; | 118 b->frees += b->recent_frees; |
98 b->alloc_bytes += b->recent_alloc_bytes; | 119 b->alloc_bytes += b->recent_alloc_bytes; |
99 b->free_bytes += b->recent_free_bytes; | 120 b->free_bytes += b->recent_free_bytes; |
100 b->recent_allocs = 0; | 121 b->recent_allocs = 0; |
101 b->recent_frees = 0; | 122 b->recent_frees = 0; |
102 b->recent_alloc_bytes = 0; | 123 b->recent_alloc_bytes = 0; |
103 b->recent_free_bytes = 0; | 124 b->recent_free_bytes = 0; |
104 } | 125 } |
105 runtime·unlock(&proflock); | 126 runtime·unlock(&proflock); |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 int32 nstk; | 242 int32 nstk; |
222 uintptr stk[32]; | 243 uintptr stk[32]; |
223 Bucket *b; | 244 Bucket *b; |
224 | 245 |
225 if(m->nomemprof > 0) | 246 if(m->nomemprof > 0) |
226 return; | 247 return; |
227 | 248 |
228 m->nomemprof++; | 249 m->nomemprof++; |
229 nstk = runtime·callers(1, stk, 32); | 250 nstk = runtime·callers(1, stk, 32); |
230 runtime·lock(&proflock); | 251 runtime·lock(&proflock); |
231 » b = stkbucket(stk, nstk, true); | 252 » b = stkbucket(MProf, stk, nstk, true); |
232 b->recent_allocs++; | 253 b->recent_allocs++; |
233 b->recent_alloc_bytes += size; | 254 b->recent_alloc_bytes += size; |
234 setaddrbucket((uintptr)p, b); | 255 setaddrbucket((uintptr)p, b); |
235 runtime·unlock(&proflock); | 256 runtime·unlock(&proflock); |
236 m->nomemprof--; | 257 m->nomemprof--; |
237 } | 258 } |
238 | 259 |
239 // Called when freeing a profiled block. | 260 // Called when freeing a profiled block. |
240 void | 261 void |
241 runtime·MProf_Free(void *p, uintptr size) | 262 runtime·MProf_Free(void *p, uintptr size) |
242 { | 263 { |
243 Bucket *b; | 264 Bucket *b; |
244 | 265 |
245 if(m->nomemprof > 0) | 266 if(m->nomemprof > 0) |
246 return; | 267 return; |
247 | 268 |
248 m->nomemprof++; | 269 m->nomemprof++; |
249 runtime·lock(&proflock); | 270 runtime·lock(&proflock); |
250 b = getaddrbucket((uintptr)p); | 271 b = getaddrbucket((uintptr)p); |
251 if(b != nil) { | 272 if(b != nil) { |
252 b->recent_frees++; | 273 b->recent_frees++; |
253 b->recent_free_bytes += size; | 274 b->recent_free_bytes += size; |
254 } | 275 } |
255 runtime·unlock(&proflock); | 276 runtime·unlock(&proflock); |
256 m->nomemprof--; | 277 m->nomemprof--; |
257 } | 278 } |
258 | 279 |
| 280 int64 runtime·blockprofilerate; // in CPU ticks |
| 281 |
| 282 void |
| 283 runtime·SetBlockProfileRate(intgo rate) |
| 284 { |
| 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; |
| 293 uintptr stk[32]; |
| 294 Bucket *b; |
| 295 |
| 296 if(cycles <= 0) |
| 297 return; |
| 298 rate = runtime·atomicload64((uint64*)&runtime·blockprofilerate); |
| 299 if(rate <= 0 || (rate > cycles && runtime·fastrand1()%rate > cycles)) |
| 300 return; |
| 301 |
| 302 nstk = runtime·callers(skip, stk, 32); |
| 303 runtime·lock(&proflock); |
| 304 b = stkbucket(BProf, stk, nstk, true); |
| 305 b->count++; |
| 306 b->cycles += cycles; |
| 307 runtime·unlock(&proflock); |
| 308 } |
259 | 309 |
260 // Go interface to profile data. (Declared in extern.go) | 310 // Go interface to profile data. (Declared in extern.go) |
261 // Assumes Go sizeof(int) == sizeof(int32) | 311 // Assumes Go sizeof(int) == sizeof(int32) |
262 | 312 |
263 // Must match MemProfileRecord in debug.go. | 313 // Must match MemProfileRecord in debug.go. |
264 typedef struct Record Record; | 314 typedef struct Record Record; |
265 struct Record { | 315 struct Record { |
266 int64 alloc_bytes, free_bytes; | 316 int64 alloc_bytes, free_bytes; |
267 int64 alloc_objects, free_objects; | 317 int64 alloc_objects, free_objects; |
268 uintptr stk[32]; | 318 uintptr stk[32]; |
(...skipping 14 matching lines...) Expand all Loading... |
283 for(; i<nelem(r->stk); i++) | 333 for(; i<nelem(r->stk); i++) |
284 r->stk[i] = 0; | 334 r->stk[i] = 0; |
285 } | 335 } |
286 | 336 |
287 func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) { | 337 func MemProfile(p Slice, include_inuse_zero bool) (n int, ok bool) { |
288 Bucket *b; | 338 Bucket *b; |
289 Record *r; | 339 Record *r; |
290 | 340 |
291 runtime·lock(&proflock); | 341 runtime·lock(&proflock); |
292 n = 0; | 342 n = 0; |
293 » for(b=buckets; b; b=b->allnext) | 343 » for(b=mbuckets; b; b=b->allnext) |
294 if(include_inuse_zero || b->alloc_bytes != b->free_bytes) | 344 if(include_inuse_zero || b->alloc_bytes != b->free_bytes) |
295 n++; | 345 n++; |
296 ok = false; | 346 ok = false; |
297 if(n <= p.len) { | 347 if(n <= p.len) { |
298 ok = true; | 348 ok = true; |
299 r = (Record*)p.array; | 349 r = (Record*)p.array; |
300 » » for(b=buckets; b; b=b->allnext) | 350 » » for(b=mbuckets; b; b=b->allnext) |
301 if(include_inuse_zero || b->alloc_bytes != b->free_bytes
) | 351 if(include_inuse_zero || b->alloc_bytes != b->free_bytes
) |
302 record(r++, b); | 352 record(r++, b); |
| 353 } |
| 354 runtime·unlock(&proflock); |
| 355 } |
| 356 |
| 357 // Must match BlockProfileRecord in debug.go. |
| 358 typedef struct BRecord BRecord; |
| 359 struct BRecord { |
| 360 int64 count; |
| 361 int64 cycles; |
| 362 uintptr stk[32]; |
| 363 }; |
| 364 |
| 365 func BlockProfile(p Slice) (n int, ok bool) { |
| 366 Bucket *b; |
| 367 BRecord *r; |
| 368 int32 i; |
| 369 |
| 370 runtime·lock(&proflock); |
| 371 n = 0; |
| 372 for(b=bbuckets; b; b=b->allnext) |
| 373 n++; |
| 374 ok = false; |
| 375 if(n <= p.len) { |
| 376 ok = true; |
| 377 r = (BRecord*)p.array; |
| 378 for(b=bbuckets; b; b=b->allnext, r++) { |
| 379 r->count = b->count; |
| 380 r->cycles = b->cycles; |
| 381 for(i=0; i<b->nstk && i<nelem(r->stk); i++) |
| 382 r->stk[i] = b->stk[i]; |
| 383 for(; i<nelem(r->stk); i++) |
| 384 r->stk[i] = 0;·················· |
| 385 } |
303 } | 386 } |
304 runtime·unlock(&proflock); | 387 runtime·unlock(&proflock); |
305 } | 388 } |
306 | 389 |
307 // Must match StackRecord in debug.go. | 390 // Must match StackRecord in debug.go. |
308 typedef struct TRecord TRecord; | 391 typedef struct TRecord TRecord; |
309 struct TRecord { | 392 struct TRecord { |
310 uintptr stk[32]; | 393 uintptr stk[32]; |
311 }; | 394 }; |
312 | 395 |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 saveg(gp->sched.pc, (byte*)gp->sched.sp, gp, r++
); | 481 saveg(gp->sched.pc, (byte*)gp->sched.sp, gp, r++
); |
399 } | 482 } |
400 } | 483 } |
401 ········ | 484 ········ |
402 m->gcing = 0; | 485 m->gcing = 0; |
403 runtime·semrelease(&runtime·worldsema); | 486 runtime·semrelease(&runtime·worldsema); |
404 runtime·starttheworld(); | 487 runtime·starttheworld(); |
405 } | 488 } |
406 } | 489 } |
407 | 490 |
LEFT | RIGHT |