OLD | NEW |
1 // Copyright (c) 2006, Google Inc. | 1 // Copyright (c) 2006, Google Inc. |
2 // All rights reserved. | 2 // All rights reserved. |
3 // | 3 // |
4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
6 // met: | 6 // met: |
7 // | 7 // |
8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 | 60 |
61 #include "base/logging.h" | 61 #include "base/logging.h" |
62 #include "raw_printer.h" | 62 #include "raw_printer.h" |
63 #include "symbolize.h" | 63 #include "symbolize.h" |
64 #include <google/stacktrace.h> | 64 #include <google/stacktrace.h> |
65 #include <google/malloc_hook.h> | 65 #include <google/malloc_hook.h> |
66 #include "base/commandlineflags.h" | 66 #include "base/commandlineflags.h" |
67 #include "base/logging.h" // for the RawFD I/O commands | 67 #include "base/logging.h" // for the RawFD I/O commands |
68 #include "base/sysinfo.h" | 68 #include "base/sysinfo.h" |
69 | 69 |
| 70 #ifdef DEEP_PROFILER_ON |
| 71 #include "deep-memory-profiler.h" |
| 72 #endif |
| 73 |
70 using std::sort; | 74 using std::sort; |
71 using std::equal; | 75 using std::equal; |
72 using std::copy; | 76 using std::copy; |
73 using std::string; | 77 using std::string; |
74 using std::map; | 78 using std::map; |
75 | 79 |
76 using tcmalloc::FillProcSelfMaps; // from sysinfo.h | 80 using tcmalloc::FillProcSelfMaps; // from sysinfo.h |
77 using tcmalloc::DumpProcSelfMaps; // from sysinfo.h | 81 using tcmalloc::DumpProcSelfMaps; // from sysinfo.h |
78 | 82 |
79 //---------------------------------------------------------------------- | 83 //---------------------------------------------------------------------- |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 | 126 |
123 HeapProfileTable::HeapProfileTable(Allocator alloc, DeAllocator dealloc) | 127 HeapProfileTable::HeapProfileTable(Allocator alloc, DeAllocator dealloc) |
124 : alloc_(alloc), dealloc_(dealloc) { | 128 : alloc_(alloc), dealloc_(dealloc) { |
125 // Make the table | 129 // Make the table |
126 const int table_bytes = kHashTableSize * sizeof(*table_); | 130 const int table_bytes = kHashTableSize * sizeof(*table_); |
127 table_ = reinterpret_cast<Bucket**>(alloc_(table_bytes)); | 131 table_ = reinterpret_cast<Bucket**>(alloc_(table_bytes)); |
128 memset(table_, 0, table_bytes); | 132 memset(table_, 0, table_bytes); |
129 // Make allocation map | 133 // Make allocation map |
130 allocation_ = | 134 allocation_ = |
131 new(alloc_(sizeof(AllocationMap))) AllocationMap(alloc_, dealloc_); | 135 new(alloc_(sizeof(AllocationMap))) AllocationMap(alloc_, dealloc_); |
| 136 |
| 137 allocation_mmap_ = |
| 138 new(alloc_(sizeof(AllocationMap))) AllocationMap(alloc_, dealloc_); |
| 139 mmap_record_ = false; |
| 140 |
132 // init the rest: | 141 // init the rest: |
133 memset(&total_, 0, sizeof(total_)); | 142 memset(&total_, 0, sizeof(total_)); |
134 num_buckets_ = 0; | 143 num_buckets_ = 0; |
135 } | 144 } |
136 | 145 |
137 HeapProfileTable::~HeapProfileTable() { | 146 HeapProfileTable::~HeapProfileTable() { |
138 // free allocation map | 147 // free allocation map |
139 allocation_->~AllocationMap(); | 148 allocation_->~AllocationMap(); |
140 dealloc_(allocation_); | 149 dealloc_(allocation_); |
141 allocation_ = NULL; | 150 allocation_ = NULL; |
| 151 |
| 152 allocation_mmap_->~AllocationMap(); |
| 153 dealloc_(allocation_mmap_); |
| 154 allocation_mmap_ = NULL; |
| 155 |
142 // free hash table | 156 // free hash table |
143 for (int b = 0; b < kHashTableSize; b++) { | 157 for (int b = 0; b < kHashTableSize; b++) { |
144 for (Bucket* x = table_[b]; x != 0; /**/) { | 158 for (Bucket* x = table_[b]; x != 0; /**/) { |
145 Bucket* b = x; | 159 Bucket* b = x; |
146 x = x->next; | 160 x = x->next; |
147 dealloc_(b->stack); | 161 dealloc_(b->stack); |
148 dealloc_(b); | 162 dealloc_(b); |
149 } | 163 } |
150 } | 164 } |
151 dealloc_(table_); | 165 dealloc_(table_); |
(...skipping 25 matching lines...) Expand all Loading... |
177 // Create new bucket | 191 // Create new bucket |
178 const size_t key_size = sizeof(key[0]) * depth; | 192 const size_t key_size = sizeof(key[0]) * depth; |
179 const void** kcopy = reinterpret_cast<const void**>(alloc_(key_size)); | 193 const void** kcopy = reinterpret_cast<const void**>(alloc_(key_size)); |
180 copy(key, key + depth, kcopy); | 194 copy(key, key + depth, kcopy); |
181 Bucket* b = reinterpret_cast<Bucket*>(alloc_(sizeof(Bucket))); | 195 Bucket* b = reinterpret_cast<Bucket*>(alloc_(sizeof(Bucket))); |
182 memset(b, 0, sizeof(*b)); | 196 memset(b, 0, sizeof(*b)); |
183 b->hash = h; | 197 b->hash = h; |
184 b->depth = depth; | 198 b->depth = depth; |
185 b->stack = kcopy; | 199 b->stack = kcopy; |
186 b->next = table_[buck]; | 200 b->next = table_[buck]; |
| 201 #ifdef DEEP_PROFILER_ON |
| 202 b->id = num_buckets_; |
| 203 b->is_logged = false; |
| 204 #endif |
187 table_[buck] = b; | 205 table_[buck] = b; |
188 num_buckets_++; | 206 num_buckets_++; |
189 return b; | 207 return b; |
190 } | 208 } |
191 | 209 |
192 void HeapProfileTable::RecordAlloc(const void* ptr, size_t bytes, | 210 void HeapProfileTable::RecordAlloc(const void* ptr, size_t bytes, |
193 int skip_count) { | 211 int skip_count) { |
194 void* key[kMaxStackDepth]; | 212 void* key[kMaxStackDepth]; |
195 int depth = MallocHook::GetCallerStackTrace( | 213 int depth = MallocHook::GetCallerStackTrace( |
196 key, kMaxStackDepth, kStripFrames + skip_count + 1); | 214 key, kMaxStackDepth, kStripFrames + skip_count + 1); |
197 RecordAllocWithStack(ptr, bytes, depth, key); | 215 RecordAllocWithStack(ptr, bytes, depth, key); |
198 } | 216 } |
199 | 217 |
200 void HeapProfileTable::RecordAllocWithStack( | 218 void HeapProfileTable::RecordAllocWithStack( |
201 const void* ptr, size_t bytes, int stack_depth, | 219 const void* ptr, size_t bytes, int stack_depth, |
202 const void* const call_stack[]) { | 220 const void* const call_stack[]) { |
203 Bucket* b = GetBucket(stack_depth, call_stack); | 221 Bucket* b = GetBucket(stack_depth, call_stack); |
204 b->allocs++; | 222 b->allocs++; |
205 b->alloc_size += bytes; | 223 b->alloc_size += bytes; |
206 total_.allocs++; | 224 total_.allocs++; |
207 total_.alloc_size += bytes; | 225 total_.alloc_size += bytes; |
208 | 226 |
209 AllocValue v; | 227 AllocValue v; |
210 v.set_bucket(b); // also did set_live(false); set_ignore(false) | 228 v.set_bucket(b); // also did set_live(false); set_ignore(false) |
211 v.bytes = bytes; | 229 v.bytes = bytes; |
212 allocation_->Insert(ptr, v); | 230 |
| 231 if(mmap_record_) |
| 232 allocation_mmap_->Insert(ptr, v); |
| 233 else |
| 234 allocation_->Insert(ptr, v); |
213 } | 235 } |
214 | 236 |
215 void HeapProfileTable::RecordFree(const void* ptr) { | 237 void HeapProfileTable::RecordFree(const void* ptr) { |
216 AllocValue v; | 238 AllocValue v; |
217 if (allocation_->FindAndRemove(ptr, &v)) { | 239 AllocationMap* a; |
| 240 if(mmap_record_) |
| 241 a = allocation_mmap_; |
| 242 else |
| 243 a = allocation_; |
| 244 |
| 245 if(a->FindAndRemove(ptr, &v)){ |
218 Bucket* b = v.bucket(); | 246 Bucket* b = v.bucket(); |
219 b->frees++; | 247 b->frees++; |
220 b->free_size += v.bytes; | 248 b->free_size += v.bytes; |
221 total_.frees++; | 249 total_.frees++; |
222 total_.free_size += v.bytes; | 250 total_.free_size += v.bytes; |
223 } | 251 } |
224 } | 252 } |
225 | 253 |
226 bool HeapProfileTable::FindAlloc(const void* ptr, size_t* object_size) const { | 254 bool HeapProfileTable::FindAlloc(const void* ptr, size_t* object_size) const { |
227 const AllocValue* alloc_value = allocation_->Find(ptr); | 255 const AllocValue* alloc_value = allocation_->Find(ptr); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
271 char* buf, int buflen, int bufsize, | 299 char* buf, int buflen, int bufsize, |
272 const char* extra, | 300 const char* extra, |
273 Stats* profile_stats) { | 301 Stats* profile_stats) { |
274 if (profile_stats != NULL) { | 302 if (profile_stats != NULL) { |
275 profile_stats->allocs += b.allocs; | 303 profile_stats->allocs += b.allocs; |
276 profile_stats->alloc_size += b.alloc_size; | 304 profile_stats->alloc_size += b.alloc_size; |
277 profile_stats->frees += b.frees; | 305 profile_stats->frees += b.frees; |
278 profile_stats->free_size += b.free_size; | 306 profile_stats->free_size += b.free_size; |
279 } | 307 } |
280 int printed = | 308 int printed = |
281 snprintf(buf + buflen, bufsize - buflen, "%6d: %8"PRId64" [%6d: %8"PRId64"]
@%s", | 309 snprintf(buf + buflen, bufsize - buflen, "%6d: %8"PRId64" [%6d: %8"PRId64"
] @%s", |
282 b.allocs - b.frees, | 310 b.allocs - b.frees, |
283 b.alloc_size - b.free_size, | 311 b.alloc_size - b.free_size, |
284 b.allocs, | 312 b.allocs, |
285 b.alloc_size, | 313 b.alloc_size, |
286 extra); | 314 extra); |
287 // If it looks like the snprintf failed, ignore the fact we printed anything | 315 // If it looks like the snprintf failed, ignore the fact we printed anything |
288 if (printed < 0 || printed >= bufsize - buflen) return buflen; | 316 if (printed < 0 || printed >= bufsize - buflen) return buflen; |
289 buflen += printed; | 317 buflen += printed; |
290 for (int d = 0; d < b.depth; d++) { | 318 for (int d = 0; d < b.depth; d++) { |
291 printed = snprintf(buf + buflen, bufsize - buflen, " 0x%08" PRIxPTR, | 319 printed = snprintf(buf + buflen, bufsize - buflen, " 0x%08" PRIxPTR, |
292 reinterpret_cast<uintptr_t>(b.stack[d])); | 320 reinterpret_cast<uintptr_t>(b.stack[d])); |
293 if (printed < 0 || printed >= bufsize - buflen) return buflen; | 321 if (printed < 0 || printed >= bufsize - buflen) return buflen; |
294 buflen += printed; | 322 buflen += printed; |
295 } | 323 } |
296 printed = snprintf(buf + buflen, bufsize - buflen, "\n"); | 324 printed = snprintf(buf + buflen, bufsize - buflen, "\n"); |
297 if (printed < 0 || printed >= bufsize - buflen) return buflen; | 325 if (printed < 0 || printed >= bufsize - buflen) return buflen; |
298 buflen += printed; | 326 buflen += printed; |
299 return buflen; | 327 return buflen; |
300 } | 328 } |
301 | 329 |
302 HeapProfileTable::Bucket** | 330 HeapProfileTable::Bucket** |
303 HeapProfileTable::MakeSortedBucketList() const { | 331 HeapProfileTable::MakeBucketList() const { |
| 332 // We allocate memory for (num_buckets_ + 1) buckets |
| 333 // because this allocations itself could create a new bucket. |
| 334 // There is no harm even if it doesn't create a new bucket. |
304 Bucket** list = | 335 Bucket** list = |
305 reinterpret_cast<Bucket**>(alloc_(sizeof(Bucket) * num_buckets_)); | 336 reinterpret_cast<Bucket**>(alloc_(sizeof(Bucket) * (num_buckets_ + 1))); |
306 | 337 |
307 int n = 0; | 338 int n = 0; |
308 for (int b = 0; b < kHashTableSize; b++) { | 339 for (int b = 0; b < kHashTableSize; b++) { |
309 for (Bucket* x = table_[b]; x != 0; x = x->next) { | 340 for (Bucket* x = table_[b]; x != 0; x = x->next) { |
310 list[n++] = x; | 341 list[n++] = x; |
311 } | 342 } |
312 } | 343 } |
313 RAW_DCHECK(n == num_buckets_, ""); | 344 RAW_DCHECK(n == num_buckets_, ""); |
314 | 345 |
| 346 return list; |
| 347 } |
| 348 |
| 349 HeapProfileTable::Bucket** |
| 350 HeapProfileTable::MakeSortedBucketList() const { |
| 351 Bucket** list = MakeBucketList(); |
315 sort(list, list + num_buckets_, ByAllocatedSpace); | 352 sort(list, list + num_buckets_, ByAllocatedSpace); |
316 | 353 |
317 return list; | 354 return list; |
318 } | 355 } |
319 | 356 |
320 void HeapProfileTable::IterateOrderedAllocContexts( | 357 void HeapProfileTable::IterateOrderedAllocContexts( |
321 AllocContextIterator callback) const { | 358 AllocContextIterator callback) const { |
322 Bucket** list = MakeSortedBucketList(); | 359 Bucket** list = MakeSortedBucketList(); |
323 AllocContextInfo info; | 360 AllocContextInfo info; |
324 for (int i = 0; i < num_buckets_; ++i) { | 361 for (int i = 0; i < num_buckets_; ++i) { |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 return true; | 461 return true; |
425 } else { | 462 } else { |
426 RAW_LOG(ERROR, "Failed dumping filtered heap profile to %s", file_name); | 463 RAW_LOG(ERROR, "Failed dumping filtered heap profile to %s", file_name); |
427 return false; | 464 return false; |
428 } | 465 } |
429 } | 466 } |
430 | 467 |
431 void HeapProfileTable::CleanupOldProfiles(const char* prefix) { | 468 void HeapProfileTable::CleanupOldProfiles(const char* prefix) { |
432 if (!FLAGS_cleanup_old_heap_profiles) | 469 if (!FLAGS_cleanup_old_heap_profiles) |
433 return; | 470 return; |
| 471 #ifndef DEEP_PROFILER_ON |
434 string pattern = string(prefix) + ".*" + kFileExt; | 472 string pattern = string(prefix) + ".*" + kFileExt; |
| 473 #else |
| 474 char buf[1000]; |
| 475 snprintf(buf, 1000,"%s.%05d.", prefix, getpid()); |
| 476 string pattern = string(buf) + ".*" + kFileExt; |
| 477 #endif |
| 478 |
435 #if defined(HAVE_GLOB_H) | 479 #if defined(HAVE_GLOB_H) |
436 glob_t g; | 480 glob_t g; |
437 const int r = glob(pattern.c_str(), GLOB_ERR, NULL, &g); | 481 const int r = glob(pattern.c_str(), GLOB_ERR, NULL, &g); |
438 if (r == 0 || r == GLOB_NOMATCH) { | 482 if (r == 0 || r == GLOB_NOMATCH) { |
439 const int prefix_length = strlen(prefix); | 483 const int prefix_length = strlen(prefix); |
440 for (int i = 0; i < g.gl_pathc; i++) { | 484 for (int i = 0; i < g.gl_pathc; i++) { |
441 const char* fname = g.gl_pathv[i]; | 485 const char* fname = g.gl_pathv[i]; |
442 if ((strlen(fname) >= prefix_length) && | 486 if ((strlen(fname) >= prefix_length) && |
443 (memcmp(fname, prefix, prefix_length) == 0)) { | 487 (memcmp(fname, prefix, prefix_length) == 0)) { |
444 RAW_VLOG(1, "Removing old heap profile %s", fname); | 488 RAW_VLOG(1, "Removing old heap profile %s", fname); |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
592 char* unused) { | 636 char* unused) { |
593 // Perhaps also log the allocation stack trace (unsymbolized) | 637 // Perhaps also log the allocation stack trace (unsymbolized) |
594 // on this line in case somebody finds it useful. | 638 // on this line in case somebody finds it useful. |
595 RAW_LOG(ERROR, "leaked %"PRIuS" byte object %p", v->bytes, ptr); | 639 RAW_LOG(ERROR, "leaked %"PRIuS" byte object %p", v->bytes, ptr); |
596 } | 640 } |
597 | 641 |
598 void HeapProfileTable::Snapshot::ReportIndividualObjects() { | 642 void HeapProfileTable::Snapshot::ReportIndividualObjects() { |
599 char unused; | 643 char unused; |
600 map_.Iterate(ReportObject, &unused); | 644 map_.Iterate(ReportObject, &unused); |
601 } | 645 } |
OLD | NEW |