forked from OSchip/llvm-project
[lsan] Make the report_objects flag more useful.
Print the list of leaked objects after each leak report. Previously we printed only a joint list of all leaked objects. As a bonus, suppressed objects are no longer reported. llvm-svn: 197977
This commit is contained in:
parent
ec543279db
commit
625875d256
|
@ -42,4 +42,4 @@ int main() {
|
|||
return 0;
|
||||
}
|
||||
// CHECK: Test alloc: [[ADDR:.*]].
|
||||
// CHECK: leaked 1337 byte object at [[ADDR]]
|
||||
// CHECK: [[ADDR]] (1337 bytes)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]].
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<Leak> leaks_;
|
||||
InternalMmapVector<LeakedObject> leaked_objects_;
|
||||
};
|
||||
|
||||
typedef InternalMmapVector<uptr> Frontier;
|
||||
|
|
Loading…
Reference in New Issue