diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc index ab368245317c..1cae226d4ec6 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc @@ -42,4 +42,4 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: leaked 1337 byte object at [[ADDR]] +// CHECK: [[ADDR]] (1337 bytes) diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc index 57d056597ee2..38219341d890 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/large_allocation_leak.cc @@ -13,6 +13,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 33554432 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (33554432 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/pointer_to_self.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/pointer_to_self.cc index 0d2818d2fa1d..c638c2b7eb83 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/pointer_to_self.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/pointer_to_self.cc @@ -13,6 +13,6 @@ int main() { fprintf(stderr, "Test alloc: %p.\n", p); } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc index fabfb4ff21a9..4a53910abd74 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/stale_stack_leak.cc @@ -36,7 +36,7 @@ void ConfirmPointerHasSurvived() { } // CHECK: Test alloc: [[ADDR:.*]]. // CHECK-sanity: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: // CHECK-sanity: Value after LSan: [[ADDR]]. diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_after_return.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_after_return.cc index 93b0ea6068ef..ffa20f399523 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_after_return.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_after_return.cc @@ -18,6 +18,6 @@ int main() { exit(0); } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc index 5a7c48bdf49a..2a5331cfda73 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_initialized.cc @@ -16,6 +16,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc index e1d045e3f79f..9fdb1fdd5075 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_globals_uninitialized.cc @@ -16,6 +16,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_poisoned_asan.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_poisoned_asan.cc index 77a4af20b83d..1a72ac223635 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_poisoned_asan.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_poisoned_asan.cc @@ -20,6 +20,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: AddressSanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_registers.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_registers.cc index a7d8a69d7173..1a90994e373e 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_registers.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_registers.cc @@ -46,6 +46,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks.cc index 4287a96b2285..aea90bba1a86 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks.cc @@ -15,6 +15,6 @@ int main() { exit(0); } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc index c7dfaf8abad6..bcb4b1e2d717 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_stacks_threaded.cc @@ -31,6 +31,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc index 2570b63f0c5e..660d076909d6 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_dynamic.cc @@ -28,6 +28,6 @@ int main(int argc, char *argv[]) { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc index 3dea41edddd4..cfb6b2dd9853 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_dynamic.cc @@ -32,6 +32,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc index b75f15153863..4def4c021bf6 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_pthread_specific_static.cc @@ -26,6 +26,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_static.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_static.cc index 9ccb2b2b7fb1..1c5512568fb1 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_static.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_tls_static.cc @@ -16,6 +16,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lit_tests/TestCases/use_unaligned.cc b/compiler-rt/lib/lsan/lit_tests/TestCases/use_unaligned.cc index bc75f11b99f0..50eb749527cf 100644 --- a/compiler-rt/lib/lsan/lit_tests/TestCases/use_unaligned.cc +++ b/compiler-rt/lib/lsan/lit_tests/TestCases/use_unaligned.cc @@ -18,6 +18,6 @@ int main() { return 0; } // CHECK: Test alloc: [[ADDR:.*]]. -// CHECK: Directly leaked 1337 byte object at [[ADDR]] // CHECK: LeakSanitizer: detected memory leaks +// CHECK: [[ADDR]] (1337 bytes) // CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: diff --git a/compiler-rt/lib/lsan/lsan_common.cc b/compiler-rt/lib/lsan/lsan_common.cc index f29897cd6115..7e9c14e400cc 100644 --- a/compiler-rt/lib/lsan/lsan_common.cc +++ b/compiler-rt/lib/lsan/lsan_common.cc @@ -373,26 +373,16 @@ static void CollectLeaksCb(uptr chunk, void *arg) { if (!m.allocated()) return; if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { uptr resolution = flags()->resolution; + u32 stack_trace_id = 0; if (resolution > 0) { uptr size = 0; const uptr *trace = StackDepotGet(m.stack_trace_id(), &size); size = Min(size, resolution); - leak_report->Add(StackDepotPut(trace, size), m.requested_size(), m.tag()); + stack_trace_id = StackDepotPut(trace, size); } else { - leak_report->Add(m.stack_trace_id(), m.requested_size(), m.tag()); + stack_trace_id = m.stack_trace_id(); } - } -} - -// ForEachChunkCallback. Prints addresses of unreachable chunks. -static void PrintLeakedCb(uptr chunk, void *arg) { - chunk = GetUserBegin(chunk); - LsanMetadata m(chunk); - if (!m.allocated()) return; - if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { - Printf("%s leaked %zu byte object at %p.\n", - m.tag() == kDirectlyLeaked ? "Directly" : "Indirectly", - m.requested_size(), chunk); + leak_report->Add(chunk, stack_trace_id, m.requested_size(), m.tag()); } } @@ -411,12 +401,6 @@ static void PrintMatchedSuppressions() { Printf("%s\n\n", line); } -static void PrintLeaked() { - Printf("\n"); - Printf("Reporting individual objects:\n"); - ForEachChunk(PrintLeakedCb, 0 /* arg */); -} - struct DoLeakCheckParam { bool success; LeakReport leak_report; @@ -430,8 +414,6 @@ static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads, CHECK(param->leak_report.IsEmpty()); ClassifyAllChunks(suspended_threads); ForEachChunk(CollectLeaksCb, ¶m->leak_report); - if (!param->leak_report.IsEmpty() && flags()->report_objects) - PrintLeaked(); param->success = true; } @@ -512,20 +494,29 @@ static Suppression *GetSuppressionForStack(u32 stack_trace_id) { // use a hash table. const uptr kMaxLeaksConsidered = 5000; -void LeakReport::Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag) { +void LeakReport::Add(uptr chunk, u32 stack_trace_id, uptr leaked_size, + ChunkTag tag) { CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked); bool is_directly_leaked = (tag == kDirectlyLeaked); - for (uptr i = 0; i < leaks_.size(); i++) + uptr i; + for (i = 0; i < leaks_.size(); i++) { if (leaks_[i].stack_trace_id == stack_trace_id && leaks_[i].is_directly_leaked == is_directly_leaked) { leaks_[i].hit_count++; leaks_[i].total_size += leaked_size; - return; + break; } - if (leaks_.size() == kMaxLeaksConsidered) return; - Leak leak = { /* hit_count */ 1, leaked_size, stack_trace_id, - is_directly_leaked, /* is_suppressed */ false }; - leaks_.push_back(leak); + } + if (i == leaks_.size()) { + if (leaks_.size() == kMaxLeaksConsidered) return; + Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id, + is_directly_leaked, /* is_suppressed */ false }; + leaks_.push_back(leak); + } + if (flags()->report_objects) { + LeakedObject obj = {leaks_[i].id, chunk, leaked_size}; + leaked_objects_.push_back(obj); + } } static bool LeakComparator(const Leak &leak1, const Leak &leak2) { @@ -559,6 +550,17 @@ void LeakReport::PrintLargest(uptr num_leaks_to_print) { leaks_[i].total_size, leaks_[i].hit_count); Printf("%s", d.End()); PrintStackTraceById(leaks_[i].stack_trace_id); + + if (flags()->report_objects) { + Printf("Objects leaked above:\n"); + for (uptr j = 0; j < leaked_objects_.size(); j++) { + if (leaked_objects_[j].id == leaks_[i].id) + Printf("%p (%zu bytes)\n", leaked_objects_[j].addr, + leaked_objects_[j].size); + } + Printf("\n"); + } + leaks_printed++; if (leaks_printed == num_leaks_to_print) break; } diff --git a/compiler-rt/lib/lsan/lsan_common.h b/compiler-rt/lib/lsan/lsan_common.h index 38a3b47f9acf..6a0c1b4bfcf3 100644 --- a/compiler-rt/lib/lsan/lsan_common.h +++ b/compiler-rt/lib/lsan/lsan_common.h @@ -82,6 +82,7 @@ extern Flags lsan_flags; inline Flags *flags() { return &lsan_flags; } struct Leak { + u32 id; uptr hit_count; uptr total_size; u32 stack_trace_id; @@ -89,17 +90,25 @@ struct Leak { bool is_suppressed; }; +struct LeakedObject { + u32 id; + uptr addr; + uptr size; +}; + // Aggregates leaks by stack trace prefix. class LeakReport { public: - LeakReport() : leaks_(1) {} - void Add(u32 stack_trace_id, uptr leaked_size, ChunkTag tag); + LeakReport() : next_id_(0), leaks_(1), leaked_objects_(1) {} + void Add(uptr chunk, u32 stack_trace_id, uptr leaked_size, ChunkTag tag); void PrintLargest(uptr max_leaks); void PrintSummary(); bool IsEmpty() { return leaks_.size() == 0; } uptr ApplySuppressions(); private: + u32 next_id_; InternalMmapVector leaks_; + InternalMmapVector leaked_objects_; }; typedef InternalMmapVector Frontier;