[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:
Sergey Matveev 2013-12-24 12:03:02 +00:00
parent ec543279db
commit 625875d256
18 changed files with 58 additions and 47 deletions

View File

@ -42,4 +42,4 @@ int main() {
return 0;
}
// CHECK: Test alloc: [[ADDR:.*]].
// CHECK: leaked 1337 byte object at [[ADDR]]
// CHECK: [[ADDR]] (1337 bytes)

View File

@ -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:

View File

@ -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:

View File

@ -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]].

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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, &param->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;
}

View File

@ -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;