mirror of
https://github.com/owntone/owntone-server.git
synced 2025-02-03 09:56:00 -05:00
Add memory leak checks
This commit is contained in:
parent
938d54b055
commit
41746a0326
171
src/err.c
171
src/err.c
@ -22,15 +22,35 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define __IN_ERR__
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct tag_err_leak {
|
||||||
|
void *ptr;
|
||||||
|
char *file;
|
||||||
|
int line;
|
||||||
|
int size;
|
||||||
|
struct tag_err_leak *next;
|
||||||
|
} ERR_LEAK;
|
||||||
|
|
||||||
int err_debuglevel=0;
|
int err_debuglevel=0;
|
||||||
int err_logdestination=LOGDEST_STDERR;
|
int err_logdestination=LOGDEST_STDERR;
|
||||||
|
|
||||||
|
pthread_mutex_t err_mutex=PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
ERR_LEAK err_leak = { NULL, NULL, 0, 0, NULL };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forwards
|
||||||
|
*/
|
||||||
|
|
||||||
|
int err_lock_mutex(void);
|
||||||
|
int err_unlock_mutex(void);
|
||||||
|
|
||||||
/****************************************************
|
/****************************************************
|
||||||
* log_err
|
* log_err
|
||||||
****************************************************/
|
****************************************************/
|
||||||
@ -62,6 +82,7 @@ void log_err(int quit, char *fmt, ...)
|
|||||||
****************************************************/
|
****************************************************/
|
||||||
void log_setdest(char *app, int destination) {
|
void log_setdest(char *app, int destination) {
|
||||||
switch(destination) {
|
switch(destination) {
|
||||||
|
|
||||||
case LOGDEST_SYSLOG:
|
case LOGDEST_SYSLOG:
|
||||||
if(err_logdestination != LOGDEST_SYSLOG) {
|
if(err_logdestination != LOGDEST_SYSLOG) {
|
||||||
openlog(app,LOG_PID,LOG_DAEMON);
|
openlog(app,LOG_PID,LOG_DAEMON);
|
||||||
@ -75,3 +96,153 @@ void log_setdest(char *app, int destination) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
/*
|
||||||
|
* err_lock
|
||||||
|
*
|
||||||
|
* Lock the error mutex
|
||||||
|
*/
|
||||||
|
int err_lock_mutex(void) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if(err=pthread_mutex_lock(&err_mutex)) {
|
||||||
|
errno=err;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* err_unlock
|
||||||
|
*
|
||||||
|
* Unlock the error mutex
|
||||||
|
*
|
||||||
|
* returns 0 on success,
|
||||||
|
* returns -1 on failure, with errno set
|
||||||
|
*/
|
||||||
|
int err_unlock_mutex(void) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if(err=pthread_mutex_unlock(&err_mutex)) {
|
||||||
|
errno=err;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* err_malloc
|
||||||
|
*
|
||||||
|
* safe malloc
|
||||||
|
*/
|
||||||
|
void *err_malloc(char *file, int line, size_t size) {
|
||||||
|
ERR_LEAK *pnew;
|
||||||
|
|
||||||
|
DPRINTF(ERR_DEBUG,"Mallocing %d bytes\n",size);
|
||||||
|
err_leakcheck();
|
||||||
|
DPRINTF(ERR_DEBUG,"---\n");
|
||||||
|
|
||||||
|
pnew=(ERR_LEAK*)malloc(sizeof(pnew));
|
||||||
|
if(!pnew)
|
||||||
|
log_err(1,"Error: cannot allocate leak struct\n");
|
||||||
|
|
||||||
|
if(err_lock_mutex())
|
||||||
|
log_err(1,"Error: cannot lock error mutex\n");
|
||||||
|
|
||||||
|
pnew->file=file;
|
||||||
|
pnew->line=line;
|
||||||
|
pnew->size=size;
|
||||||
|
pnew->ptr=malloc(size);
|
||||||
|
|
||||||
|
pnew->next=err_leak.next;
|
||||||
|
err_leak.next=pnew;
|
||||||
|
|
||||||
|
err_unlock_mutex();
|
||||||
|
|
||||||
|
DPRINTF(ERR_DEBUG,"Malloced at %x\n",pnew->ptr);
|
||||||
|
err_leakcheck();
|
||||||
|
DPRINTF(ERR_DEBUG,"---\n");
|
||||||
|
|
||||||
|
return pnew->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* err_strdup
|
||||||
|
*
|
||||||
|
* safe strdup
|
||||||
|
*/
|
||||||
|
char *err_strdup(char *file, int line, const char *str) {
|
||||||
|
void *pnew;
|
||||||
|
|
||||||
|
pnew=err_malloc(file,line,strlen(str) + 1);
|
||||||
|
if(!pnew)
|
||||||
|
log_err(1,"Cannot malloc enough space for strdup\n");
|
||||||
|
|
||||||
|
memcpy(pnew,str,strlen(str)+1);
|
||||||
|
return pnew;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* err_free
|
||||||
|
*
|
||||||
|
* safe free
|
||||||
|
*/
|
||||||
|
void err_free(char *file, int line, void *ptr) {
|
||||||
|
ERR_LEAK *current,*last;
|
||||||
|
|
||||||
|
DPRINTF(ERR_DEBUG,"Freeing %x\n",ptr);
|
||||||
|
err_leakcheck();
|
||||||
|
DPRINTF(ERR_DEBUG,"---\n");
|
||||||
|
|
||||||
|
if(err_lock_mutex())
|
||||||
|
log_err(1,"Error: cannot lock error mutex\n");
|
||||||
|
|
||||||
|
last=&err_leak;
|
||||||
|
current=last->next;
|
||||||
|
|
||||||
|
while((current) && (current->ptr != ptr)) {
|
||||||
|
last=current;
|
||||||
|
current=current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!current) {
|
||||||
|
log_err(0,"Attempt to free unallocated memory: %s, %d\n",file,line);
|
||||||
|
} else {
|
||||||
|
free(current->ptr);
|
||||||
|
last->next=current->next;
|
||||||
|
free(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
err_unlock_mutex();
|
||||||
|
|
||||||
|
DPRINTF(ERR_DEBUG,"Freed\n");
|
||||||
|
err_leakcheck();
|
||||||
|
DPRINTF(ERR_DEBUG,"---\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* void err_leakcheck
|
||||||
|
*
|
||||||
|
* Walk through the list of memory
|
||||||
|
*/
|
||||||
|
void err_leakcheck(void) {
|
||||||
|
ERR_LEAK *current;
|
||||||
|
|
||||||
|
if(err_lock_mutex())
|
||||||
|
log_err(1,"Error: cannot lock error mutex\n");
|
||||||
|
|
||||||
|
current=err_leak.next;
|
||||||
|
while(current) {
|
||||||
|
printf("%s: %d - %d bytes at %x\n",current->file, current->line, current->size,
|
||||||
|
current->ptr);
|
||||||
|
current=current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
err_unlock_mutex();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
19
src/err.h
19
src/err.h
@ -37,10 +37,25 @@ extern void log_err(int quit, char *fmt, ...);
|
|||||||
extern void log_setdest(char *app, int destination);
|
extern void log_setdest(char *app, int destination);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define DPRINTF(level, fmt, arg...) \
|
# define DPRINTF(level, fmt, arg...) \
|
||||||
{ if((level) <= err_debuglevel) { log_err(0,"%s: ",__FILE__); log_err(0,fmt,##arg); }}
|
{ if((level) <= err_debuglevel) { log_err(0,"%s: ",__FILE__); log_err(0,fmt,##arg); }}
|
||||||
#else
|
#else
|
||||||
#define DPRINTF(level, fmt, arg...)
|
# define DPRINTF(level, fmt, arg...)
|
||||||
|
#endif /* DEBUG */
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
# ifndef __IN_ERR__
|
||||||
|
# define malloc(x) err_malloc(__FILE__,__LINE__,x)
|
||||||
|
# define strdup(x) err_strdup(__FILE__,__LINE__,x)
|
||||||
|
# define free(x) err_free(__FILE__,__LINE__,x)
|
||||||
|
# endif /* __IN_ERR__ */
|
||||||
|
|
||||||
|
|
||||||
|
extern void *err_malloc(char *file, int line, size_t size);
|
||||||
|
extern char *err_strdup(char *file, int line, const char *str);
|
||||||
|
extern void err_free(char *file, int line, void *ptr);
|
||||||
|
extern void err_leakcheck(void);
|
||||||
|
|
||||||
#endif /* DEBUG */
|
#endif /* DEBUG */
|
||||||
|
|
||||||
#endif /* __ERR_H__ */
|
#endif /* __ERR_H__ */
|
||||||
|
@ -349,9 +349,13 @@ int main(int argc, char *argv[]) {
|
|||||||
rend_init(&rendezvous_pid,config.servername, config.port);
|
rend_init(&rendezvous_pid,config.servername, config.port);
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
sleep(20);
|
sleep(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
err_leakcheck();
|
||||||
|
#endif
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,10 @@ typedef struct tag_ws_private {
|
|||||||
int stop;
|
int stop;
|
||||||
int running;
|
int running;
|
||||||
int threadno;
|
int threadno;
|
||||||
|
int dispatch_threads;
|
||||||
pthread_t server_tid;
|
pthread_t server_tid;
|
||||||
|
pthread_cond_t exit_cond;
|
||||||
|
pthread_mutex_t exit_mutex;
|
||||||
} WS_PRIVATE;
|
} WS_PRIVATE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -101,7 +104,8 @@ int ws_testrequestheader(WS_CONNINFO *pwsc, char *header, char *value);
|
|||||||
/*
|
/*
|
||||||
* Globals
|
* Globals
|
||||||
*/
|
*/
|
||||||
pthread_mutex_t munsafe=PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t ws_unsafe=PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
char *ws_dow[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
char *ws_dow[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||||||
char *ws_moy[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
|
char *ws_moy[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
|
||||||
"Aug", "Sep", "Oct", "Nov", "Dec" };
|
"Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||||
@ -117,7 +121,7 @@ char *ws_moy[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
|
|||||||
int ws_lock_unsafe(void) {
|
int ws_lock_unsafe(void) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if(err=pthread_mutex_lock(&munsafe)) {
|
if(err=pthread_mutex_lock(&ws_unsafe)) {
|
||||||
errno=err;
|
errno=err;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -136,7 +140,7 @@ int ws_lock_unsafe(void) {
|
|||||||
int ws_unlock_unsafe(void) {
|
int ws_unlock_unsafe(void) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if(err=pthread_mutex_unlock(&munsafe)) {
|
if(err=pthread_mutex_unlock(&ws_unsafe)) {
|
||||||
errno=err;
|
errno=err;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -168,8 +172,19 @@ WSHANDLE ws_start(WSCONFIG *config) {
|
|||||||
memcpy(&pwsp->wsconfig,config,sizeof(WS_PRIVATE));
|
memcpy(&pwsp->wsconfig,config,sizeof(WS_PRIVATE));
|
||||||
pwsp->running=0;
|
pwsp->running=0;
|
||||||
pwsp->threadno=0;
|
pwsp->threadno=0;
|
||||||
|
pwsp->dispatch_threads=0;
|
||||||
pwsp->handlers.next=NULL;
|
pwsp->handlers.next=NULL;
|
||||||
|
|
||||||
|
if(err=pthread_cond_init(&pwsp->exit_cond, NULL)) {
|
||||||
|
errno=err;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(err=pthread_mutex_init(&pwsp->exit_mutex,NULL)) {
|
||||||
|
errno=err;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
DPRINTF(ERR_INFO,"Preparing to listen on port %d\n",pwsp->wsconfig.port);
|
DPRINTF(ERR_INFO,"Preparing to listen on port %d\n",pwsp->wsconfig.port);
|
||||||
|
|
||||||
if((pwsp->server_fd = u_open(pwsp->wsconfig.port)) == -1) {
|
if((pwsp->server_fd = u_open(pwsp->wsconfig.port)) == -1) {
|
||||||
@ -198,10 +213,23 @@ WSHANDLE ws_start(WSCONFIG *config) {
|
|||||||
*
|
*
|
||||||
* Stop the web server and all the child threads
|
* Stop the web server and all the child threads
|
||||||
*/
|
*/
|
||||||
int ws_stop(WSHANDLE arg) {
|
extern int ws_stop(WSHANDLE ws) {
|
||||||
WS_PRIVATE *pwsp = (WS_PRIVATE*)arg;
|
WS_PRIVATE *pwsp = (WS_PRIVATE*)ws;
|
||||||
|
|
||||||
/* free the ws_handlers */
|
/* free the ws_handlers */
|
||||||
|
pwsp->stop=1;
|
||||||
|
|
||||||
|
/* Wait for all the threads to die */
|
||||||
|
if(pthread_mutex_lock(&pwsp->exit_mutex))
|
||||||
|
log_err(1,"Cannot lock condition mutex\n");
|
||||||
|
|
||||||
|
/* wait for condition */
|
||||||
|
while(pwsp->dispatch_threads) {
|
||||||
|
pthread_cond_wait(&pwsp->exit_cond, &pwsp->exit_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&pwsp->exit_mutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,13 +285,19 @@ void *ws_mainthread(void *arg) {
|
|||||||
ws_unlock_unsafe();
|
ws_unlock_unsafe();
|
||||||
|
|
||||||
/* now, throw off a dispatch thread */
|
/* now, throw off a dispatch thread */
|
||||||
|
if(pthread_mutex_lock(&pwsp->exit_mutex))
|
||||||
|
log_err(1,"Cannot lock condition mutex\n");
|
||||||
|
|
||||||
if(err=pthread_create(&tid,NULL,ws_dispatcher,(void*)pwsc)) {
|
if(err=pthread_create(&tid,NULL,ws_dispatcher,(void*)pwsc)) {
|
||||||
pwsc->error=err;
|
pwsc->error=err;
|
||||||
DPRINTF(ERR_WARN,"Could not spawn thread: %s\n",strerror(err));
|
DPRINTF(ERR_WARN,"Could not spawn thread: %s\n",strerror(err));
|
||||||
|
pthread_mutex_unlock(&pwsp->exit_mutex);
|
||||||
ws_close(pwsc);
|
ws_close(pwsc);
|
||||||
|
} else {
|
||||||
|
pwsp->dispatch_threads++;
|
||||||
|
pthread_mutex_unlock(&pwsp->exit_mutex);
|
||||||
|
pthread_detach(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_detach(tid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,6 +314,8 @@ void *ws_mainthread(void *arg) {
|
|||||||
* allocated memory has been freed
|
* allocated memory has been freed
|
||||||
*/
|
*/
|
||||||
void ws_close(WS_CONNINFO *pwsc) {
|
void ws_close(WS_CONNINFO *pwsc) {
|
||||||
|
WS_PRIVATE *pwsp = (WS_PRIVATE *)pwsc->pwsp;
|
||||||
|
|
||||||
DPRINTF(ERR_DEBUG,"Thread %d: Terminating\n",pwsc->threadno);
|
DPRINTF(ERR_DEBUG,"Thread %d: Terminating\n",pwsc->threadno);
|
||||||
DPRINTF(ERR_DEBUG,"Thread %d: Freeing request headers\n",pwsc->threadno);
|
DPRINTF(ERR_DEBUG,"Thread %d: Freeing request headers\n",pwsc->threadno);
|
||||||
ws_freearglist(&pwsc->request_headers);
|
ws_freearglist(&pwsc->request_headers);
|
||||||
@ -292,6 +328,20 @@ void ws_close(WS_CONNINFO *pwsc) {
|
|||||||
DPRINTF(ERR_DEBUG,"Thread %d: Closing fd\n",pwsc->threadno);
|
DPRINTF(ERR_DEBUG,"Thread %d: Closing fd\n",pwsc->threadno);
|
||||||
close(pwsc->fd);
|
close(pwsc->fd);
|
||||||
free(pwsc->hostname);
|
free(pwsc->hostname);
|
||||||
|
|
||||||
|
/* this thread is done */
|
||||||
|
if(pthread_mutex_lock(&pwsp->exit_mutex))
|
||||||
|
log_err(1,"Error: cannot lock condition mutex\n");
|
||||||
|
|
||||||
|
if(!pwsp->dispatch_threads) {
|
||||||
|
log_err(1,"Error: Bad dispatch thread count!\n");
|
||||||
|
} else {
|
||||||
|
pwsp->dispatch_threads--;
|
||||||
|
if(pthread_cond_signal(&pwsp->exit_cond))
|
||||||
|
log_err(1,"Error: cannot signal condition\n");
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&pwsp->exit_mutex);
|
||||||
|
|
||||||
free(pwsc);
|
free(pwsc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -673,8 +723,10 @@ void *ws_dispatcher(void *arg) {
|
|||||||
ws_defaulthandler(pwsp,pwsc);
|
ws_defaulthandler(pwsp,pwsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((pwsc->close)||(pwsc->error))
|
if((pwsc->close) || (pwsc->error) || (pwsp->stop)) {
|
||||||
|
pwsc->close=1;
|
||||||
connection_done=1;
|
connection_done=1;
|
||||||
|
}
|
||||||
ws_close(pwsc);
|
ws_close(pwsc);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user