leak-detective: Use callback functions to report leaks and usage information
This is more flexible than printing reports to a FILE.
This commit is contained in:
@@ -489,6 +489,25 @@ static void stroke_leases(private_stroke_socket_t *this,
|
|||||||
this->list->leases(this->list, msg, out);
|
this->list->leases(this->list, msg, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for usage report
|
||||||
|
*/
|
||||||
|
static void report_usage(FILE *out, int count, size_t bytes,
|
||||||
|
backtrace_t *bt, bool detailed)
|
||||||
|
{
|
||||||
|
fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
|
||||||
|
bytes, count, bytes / count);
|
||||||
|
bt->log(bt, out, detailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for memusage summary
|
||||||
|
*/
|
||||||
|
static void sum_usage(FILE *out, int count, size_t bytes, int whitelisted)
|
||||||
|
{
|
||||||
|
fprintf(out, "Total memory usage: %zu\n", bytes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show memory usage
|
* Show memory usage
|
||||||
*/
|
*/
|
||||||
@@ -497,7 +516,9 @@ static void stroke_memusage(private_stroke_socket_t *this,
|
|||||||
{
|
{
|
||||||
if (lib->leak_detective)
|
if (lib->leak_detective)
|
||||||
{
|
{
|
||||||
lib->leak_detective->usage(lib->leak_detective, out);
|
lib->leak_detective->usage(lib->leak_detective,
|
||||||
|
(leak_detective_report_cb_t)report_usage,
|
||||||
|
(leak_detective_summary_cb_t)sum_usage, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,39 @@ struct private_library_t {
|
|||||||
*/
|
*/
|
||||||
library_t *lib = NULL;
|
library_t *lib = NULL;
|
||||||
|
|
||||||
|
#ifdef LEAK_DETECTIVE
|
||||||
|
/**
|
||||||
|
* Default leak report callback
|
||||||
|
*/
|
||||||
|
static void report_leaks(void *user, int count, size_t bytes,
|
||||||
|
backtrace_t *bt, bool detailed)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%zu bytes total, %d allocations, %zu bytes average:\n",
|
||||||
|
bytes, count, bytes / count);
|
||||||
|
bt->log(bt, stderr, detailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default leak report summary callback
|
||||||
|
*/
|
||||||
|
static void sum_leaks(void* user, int count, size_t bytes, int whitelisted)
|
||||||
|
{
|
||||||
|
switch (count)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
fprintf(stderr, "No leaks detected");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
fprintf(stderr, "One leak detected");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%d leaks detected, %zu bytes", count, bytes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
|
||||||
|
}
|
||||||
|
#endif /* LEAK_DETECTIVE */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deinitialize library
|
* Deinitialize library
|
||||||
*/
|
*/
|
||||||
@@ -227,6 +260,8 @@ bool library_init(char *settings)
|
|||||||
|
|
||||||
#ifdef LEAK_DETECTIVE
|
#ifdef LEAK_DETECTIVE
|
||||||
lib->leak_detective = leak_detective_create();
|
lib->leak_detective = leak_detective_create();
|
||||||
|
lib->leak_detective->set_report_cb(lib->leak_detective,
|
||||||
|
report_leaks, sum_leaks, NULL);
|
||||||
#endif /* LEAK_DETECTIVE */
|
#endif /* LEAK_DETECTIVE */
|
||||||
|
|
||||||
pfh = printf_hook_create();
|
pfh = printf_hook_create();
|
||||||
|
|||||||
@@ -21,12 +21,12 @@
|
|||||||
#ifndef BACKTRACE_H_
|
#ifndef BACKTRACE_H_
|
||||||
#define BACKTRACE_H_
|
#define BACKTRACE_H_
|
||||||
|
|
||||||
|
typedef struct backtrace_t backtrace_t;
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <library.h>
|
#include <library.h>
|
||||||
|
|
||||||
typedef struct backtrace_t backtrace_t;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A backtrace registers the frames on the stack during creation.
|
* A backtrace registers the frames on the stack during creation.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -59,6 +59,21 @@ struct private_leak_detective_t {
|
|||||||
* public functions
|
* public functions
|
||||||
*/
|
*/
|
||||||
leak_detective_t public;
|
leak_detective_t public;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered report() function
|
||||||
|
*/
|
||||||
|
leak_detective_report_cb_t report_cb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered report() summary function
|
||||||
|
*/
|
||||||
|
leak_detective_summary_cb_t report_scb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered user data for callbacks
|
||||||
|
*/
|
||||||
|
void *report_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -599,7 +614,8 @@ static bool equals(backtrace_t *a, backtrace_t *b)
|
|||||||
* Summarize and print backtraces
|
* Summarize and print backtraces
|
||||||
*/
|
*/
|
||||||
static int print_traces(private_leak_detective_t *this,
|
static int print_traces(private_leak_detective_t *this,
|
||||||
FILE *out, int thresh, int thresh_count,
|
leak_detective_report_cb_t cb, void *user,
|
||||||
|
int thresh, int thresh_count,
|
||||||
bool detailed, int *whitelisted, size_t *sum)
|
bool detailed, int *whitelisted, size_t *sum)
|
||||||
{
|
{
|
||||||
int leaks = 0;
|
int leaks = 0;
|
||||||
@@ -652,16 +668,20 @@ static int print_traces(private_leak_detective_t *this,
|
|||||||
leaks++;
|
leaks++;
|
||||||
}
|
}
|
||||||
lock->unlock(lock);
|
lock->unlock(lock);
|
||||||
|
|
||||||
enumerator = entries->create_enumerator(entries);
|
enumerator = entries->create_enumerator(entries);
|
||||||
while (enumerator->enumerate(enumerator, NULL, &entry))
|
while (enumerator->enumerate(enumerator, NULL, &entry))
|
||||||
{
|
{
|
||||||
if (out &&
|
if (cb)
|
||||||
(!thresh || entry->bytes >= thresh) &&
|
|
||||||
(!thresh_count || entry->count >= thresh_count))
|
|
||||||
{
|
{
|
||||||
fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
|
if (!thresh || entry->bytes >= thresh)
|
||||||
entry->bytes, entry->count, entry->bytes / entry->count);
|
{
|
||||||
entry->backtrace->log(entry->backtrace, out, detailed);
|
if (!thresh_count || entry->count >= thresh_count)
|
||||||
|
{
|
||||||
|
this->report_cb(this->report_data, entry->count,
|
||||||
|
entry->bytes, entry->backtrace, detailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
entry->backtrace->destroy(entry->backtrace);
|
entry->backtrace->destroy(entry->backtrace);
|
||||||
free(entry);
|
free(entry);
|
||||||
@@ -681,38 +701,30 @@ METHOD(leak_detective_t, report, void,
|
|||||||
int leaks, whitelisted = 0;
|
int leaks, whitelisted = 0;
|
||||||
size_t sum = 0;
|
size_t sum = 0;
|
||||||
|
|
||||||
leaks = print_traces(this, stderr, 0, 0, detailed, &whitelisted, &sum);
|
leaks = print_traces(this, this->report_cb, this->report_data,
|
||||||
switch (leaks)
|
0, 0, detailed, &whitelisted, &sum);
|
||||||
|
if (this->report_scb)
|
||||||
{
|
{
|
||||||
case 0:
|
this->report_scb(this->report_data, leaks, sum, whitelisted);
|
||||||
fprintf(stderr, "No leaks detected");
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
fprintf(stderr, "One leak detected");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "%d leaks detected, %zu bytes", leaks, sum);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Leak detective disabled\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
METHOD(leak_detective_t, set_report_cb, void,
|
||||||
|
private_leak_detective_t *this, leak_detective_report_cb_t cb,
|
||||||
|
leak_detective_summary_cb_t scb, void *user)
|
||||||
|
{
|
||||||
|
this->report_cb = cb;
|
||||||
|
this->report_scb = scb;
|
||||||
|
this->report_data = user;
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(leak_detective_t, leaks, int,
|
METHOD(leak_detective_t, leaks, int,
|
||||||
private_leak_detective_t *this)
|
private_leak_detective_t *this)
|
||||||
{
|
{
|
||||||
if (lib->leak_detective)
|
int whitelisted = 0;
|
||||||
{
|
|
||||||
int leaks, whitelisted = 0;
|
|
||||||
|
|
||||||
leaks = print_traces(this, NULL, 0, 0, FALSE, &whitelisted, NULL);
|
return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL);
|
||||||
return leaks;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(leak_detective_t, set_state, bool,
|
METHOD(leak_detective_t, set_state, bool,
|
||||||
@@ -722,10 +734,11 @@ METHOD(leak_detective_t, set_state, bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
METHOD(leak_detective_t, usage, void,
|
METHOD(leak_detective_t, usage, void,
|
||||||
private_leak_detective_t *this, FILE *out)
|
private_leak_detective_t *this, leak_detective_report_cb_t cb,
|
||||||
|
leak_detective_summary_cb_t scb, void *user)
|
||||||
{
|
{
|
||||||
bool detailed;
|
bool detailed;
|
||||||
int thresh, thresh_count;
|
int thresh, thresh_count, leaks, whitelisted = 0;
|
||||||
size_t sum = 0;
|
size_t sum = 0;
|
||||||
|
|
||||||
thresh = lib->settings->get_int(lib->settings,
|
thresh = lib->settings->get_int(lib->settings,
|
||||||
@@ -735,9 +748,12 @@ METHOD(leak_detective_t, usage, void,
|
|||||||
detailed = lib->settings->get_bool(lib->settings,
|
detailed = lib->settings->get_bool(lib->settings,
|
||||||
"libstrongswan.leak_detective.detailed", TRUE);
|
"libstrongswan.leak_detective.detailed", TRUE);
|
||||||
|
|
||||||
print_traces(this, out, thresh, thresh_count, detailed, NULL, &sum);
|
leaks = print_traces(this, cb, user, thresh, thresh_count,
|
||||||
|
detailed, &whitelisted, &sum);
|
||||||
fprintf(out, "Total memory usage: %zu\n", sum);
|
if (scb)
|
||||||
|
{
|
||||||
|
scb(user, leaks, sum, whitelisted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -936,8 +952,9 @@ leak_detective_t *leak_detective_create()
|
|||||||
INIT(this,
|
INIT(this,
|
||||||
.public = {
|
.public = {
|
||||||
.report = _report,
|
.report = _report,
|
||||||
.leaks = _leaks,
|
.set_report_cb = _set_report_cb,
|
||||||
.usage = _usage,
|
.usage = _usage,
|
||||||
|
.leaks = _leaks,
|
||||||
.set_state = _set_state,
|
.set_state = _set_state,
|
||||||
.destroy = _destroy,
|
.destroy = _destroy,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,6 +24,30 @@
|
|||||||
typedef struct leak_detective_t leak_detective_t;
|
typedef struct leak_detective_t leak_detective_t;
|
||||||
|
|
||||||
#include <library.h>
|
#include <library.h>
|
||||||
|
#include <utils/backtrace.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to report leak/usage information
|
||||||
|
*
|
||||||
|
* @param user user specific data
|
||||||
|
* @param count number of allocations
|
||||||
|
* @param bytes total size of allocations
|
||||||
|
* @param bt backtrace of allocation
|
||||||
|
* @param detailed TRUE to show a detailed backtrace
|
||||||
|
*/
|
||||||
|
typedef void (*leak_detective_report_cb_t)(void *user, int count, size_t bytes,
|
||||||
|
backtrace_t *bt, bool detailed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to report leak/usage summary information
|
||||||
|
*
|
||||||
|
* @param user user specific data
|
||||||
|
* @param count total number of allocations
|
||||||
|
* @param bytes total size of all reported allocations
|
||||||
|
* @param whitelisted number of allocations suppressed by whitelist
|
||||||
|
*/
|
||||||
|
typedef void (*leak_detective_summary_cb_t)(void* user, int count, size_t bytes,
|
||||||
|
int whitelisted);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leak detective finds leaks and bad frees using malloc hooks.
|
* Leak detective finds leaks and bad frees using malloc hooks.
|
||||||
@@ -36,12 +60,33 @@ typedef struct leak_detective_t leak_detective_t;
|
|||||||
struct leak_detective_t {
|
struct leak_detective_t {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report leaks to stderr.
|
* Report leaks to the registered callback functions.
|
||||||
*
|
*
|
||||||
* @param detailed TRUE to resolve line/filename of leak (slow)
|
* @param detailed TRUE to resolve line/filename of leak (slow)
|
||||||
*/
|
*/
|
||||||
void (*report)(leak_detective_t *this, bool detailed);
|
void (*report)(leak_detective_t *this, bool detailed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report current memory usage to out.
|
||||||
|
* Set callback functions invoked during a report().
|
||||||
|
*
|
||||||
|
* @param cb callback invoked for each detected leak
|
||||||
|
* @param scb summary callback invoked at end of report
|
||||||
|
* @param user user data to supply to callbacks
|
||||||
|
*/
|
||||||
|
void (*set_report_cb)(leak_detective_t *this, leak_detective_report_cb_t cb,
|
||||||
|
leak_detective_summary_cb_t scb, void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report current memory usage using a callbacks.
|
||||||
|
*
|
||||||
|
* @param cb callback invoked for each allocation
|
||||||
|
* @param scb summary callback invoked at end of usage report
|
||||||
|
* @param user user data supplied to callbacks
|
||||||
|
*/
|
||||||
|
void (*usage)(leak_detective_t *this, leak_detective_report_cb_t cb,
|
||||||
|
leak_detective_summary_cb_t scb, void *user);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of detected leaks.
|
* Number of detected leaks.
|
||||||
*
|
*
|
||||||
@@ -49,13 +94,6 @@ struct leak_detective_t {
|
|||||||
*/
|
*/
|
||||||
int (*leaks)(leak_detective_t *this);
|
int (*leaks)(leak_detective_t *this);
|
||||||
|
|
||||||
/**
|
|
||||||
* Report current memory usage to out.
|
|
||||||
*
|
|
||||||
* @param out target to write usage report to
|
|
||||||
*/
|
|
||||||
void (*usage)(leak_detective_t *this, FILE *out);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable/disable leak detective hooks for the current thread.
|
* Enable/disable leak detective hooks for the current thread.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user