mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-24 13:13:17 -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 <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define __IN_ERR__
|
||||
#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_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
|
||||
****************************************************/
|
||||
@ -62,6 +82,7 @@ void log_err(int quit, char *fmt, ...)
|
||||
****************************************************/
|
||||
void log_setdest(char *app, int destination) {
|
||||
switch(destination) {
|
||||
|
||||
case LOGDEST_SYSLOG:
|
||||
if(err_logdestination != LOGDEST_SYSLOG) {
|
||||
openlog(app,LOG_PID,LOG_DAEMON);
|
||||
@ -75,3 +96,153 @@ void log_setdest(char *app, int destination) {
|
||||
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);
|
||||
|
||||
#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); }}
|
||||
#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 /* __ERR_H__ */
|
||||
|
@ -349,9 +349,13 @@ int main(int argc, char *argv[]) {
|
||||
rend_init(&rendezvous_pid,config.servername, config.port);
|
||||
|
||||
while(1) {
|
||||
sleep(20);
|
||||
sleep(100);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
err_leakcheck();
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,10 @@ typedef struct tag_ws_private {
|
||||
int stop;
|
||||
int running;
|
||||
int threadno;
|
||||
int dispatch_threads;
|
||||
pthread_t server_tid;
|
||||
pthread_cond_t exit_cond;
|
||||
pthread_mutex_t exit_mutex;
|
||||
} WS_PRIVATE;
|
||||
|
||||
/*
|
||||
@ -101,7 +104,8 @@ int ws_testrequestheader(WS_CONNINFO *pwsc, char *header, char *value);
|
||||
/*
|
||||
* 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_moy[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
|
||||
"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 err;
|
||||
|
||||
if(err=pthread_mutex_lock(&munsafe)) {
|
||||
if(err=pthread_mutex_lock(&ws_unsafe)) {
|
||||
errno=err;
|
||||
return -1;
|
||||
}
|
||||
@ -136,7 +140,7 @@ int ws_lock_unsafe(void) {
|
||||
int ws_unlock_unsafe(void) {
|
||||
int err;
|
||||
|
||||
if(err=pthread_mutex_unlock(&munsafe)) {
|
||||
if(err=pthread_mutex_unlock(&ws_unsafe)) {
|
||||
errno=err;
|
||||
return -1;
|
||||
}
|
||||
@ -168,8 +172,19 @@ WSHANDLE ws_start(WSCONFIG *config) {
|
||||
memcpy(&pwsp->wsconfig,config,sizeof(WS_PRIVATE));
|
||||
pwsp->running=0;
|
||||
pwsp->threadno=0;
|
||||
pwsp->dispatch_threads=0;
|
||||
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);
|
||||
|
||||
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
|
||||
*/
|
||||
int ws_stop(WSHANDLE arg) {
|
||||
WS_PRIVATE *pwsp = (WS_PRIVATE*)arg;
|
||||
extern int ws_stop(WSHANDLE ws) {
|
||||
WS_PRIVATE *pwsp = (WS_PRIVATE*)ws;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -257,13 +285,19 @@ void *ws_mainthread(void *arg) {
|
||||
ws_unlock_unsafe();
|
||||
|
||||
/* 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)) {
|
||||
pwsc->error=err;
|
||||
DPRINTF(ERR_WARN,"Could not spawn thread: %s\n",strerror(err));
|
||||
pthread_mutex_unlock(&pwsp->exit_mutex);
|
||||
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
|
||||
*/
|
||||
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: Freeing request headers\n",pwsc->threadno);
|
||||
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);
|
||||
close(pwsc->fd);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -673,8 +723,10 @@ void *ws_dispatcher(void *arg) {
|
||||
ws_defaulthandler(pwsp,pwsc);
|
||||
}
|
||||
|
||||
if((pwsc->close)||(pwsc->error))
|
||||
if((pwsc->close) || (pwsc->error) || (pwsp->stop)) {
|
||||
pwsc->close=1;
|
||||
connection_done=1;
|
||||
}
|
||||
ws_close(pwsc);
|
||||
}
|
||||
return NULL;
|
||||
|
Loading…
x
Reference in New Issue
Block a user