/** * Navit, a modular navigation system. * Copyright (C) 2005-2008 Navit Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #ifndef _MSC_VER #include #endif /* _MSC_VER */ #include "config.h" #include "file.h" #include "item.h" #include "debug.h" #ifdef HAVE_API_ANDROID #include #endif #if defined HAVE_API_WIN32_CE || defined _MSC_VER #include #include #endif #ifdef HAVE_SOCKET #include #include #include #include static int debug_socket=-1; static struct sockaddr_in debug_sin; #endif #define DEFAULT_DEBUG_LEVEL lvl_error dbg_level max_debug_level=DEFAULT_DEBUG_LEVEL; #define GLOBAL_DEBUG_LEVEL_UNSET lvl_unset dbg_level global_debug_level=GLOBAL_DEBUG_LEVEL_UNSET; int segv_level=0; int timestamp_prefix=0; static int dummy; static GHashTable *debug_hash; static gchar *gdb_program; static FILE *debug_fp; #if defined(_WIN32) || defined(__CEGCC__) static void sigsegv(int sig) { } #else #include static void sigsegv(int sig) { char buffer[256]; int retval; if (segv_level > 1) sprintf(buffer, "gdb -ex bt %s %d", gdb_program, getpid()); else sprintf(buffer, "gdb -ex bt -ex detach -ex quit %s %d", gdb_program, getpid()); retval = system(buffer); fprintf(stderr, "calling gdb returned %d\n", retval); exit(1); } #endif void debug_init(const char *program_name) { #ifndef HAVE_API_ANDROID gdb_program=g_strdup(program_name); signal(SIGSEGV, sigsegv); #endif debug_hash=g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); #ifdef __IPHONE_OS_VERSION_MIN_REQUIRED debug_fp = stdout; #else debug_fp = stderr; #endif } static void debug_update_level(gpointer key, gpointer value, gpointer user_data) { if (max_debug_level < GPOINTER_TO_INT(value)) max_debug_level = GPOINTER_TO_INT(value); } void debug_set_global_level(dbg_level level, int override_old_value ) { if (global_debug_level == GLOBAL_DEBUG_LEVEL_UNSET || override_old_value) { global_debug_level=level; if (max_debug_level < global_debug_level) { max_debug_level = global_debug_level; } } } void debug_level_set(const char *name, dbg_level level) { if (!strcmp(name, "segv")) { #ifndef HAVE_API_ANDROID segv_level=level; if (segv_level) signal(SIGSEGV, sigsegv); else signal(SIGSEGV, NULL); #endif } else if (!strcmp(name, "timestamps")) { timestamp_prefix=level; } else if (!strcmp(name, DEBUG_MODULE_GLOBAL)) { debug_set_global_level(level, 0); } else { g_hash_table_insert(debug_hash, g_strdup(name), GINT_TO_POINTER(level)); g_hash_table_foreach(debug_hash, debug_update_level, NULL); } } static dbg_level parse_dbg_level(struct attr *dbg_level_attr, struct attr *level_attr) { if (dbg_level_attr) { if(!strcmp(dbg_level_attr->u.str,"error")) { return lvl_error; } if(!strcmp(dbg_level_attr->u.str,"warning")) { return lvl_warning; } if(!strcmp(dbg_level_attr->u.str,"info")) { return lvl_info; } if(!strcmp(dbg_level_attr->u.str,"debug")) { return lvl_debug; } dbg(lvl_error, "Invalid debug level in config: '%s'", dbg_level_attr->u.str); } else if (level_attr) { if (level_attr->u.num>= lvl_error && level_attr->u.num<= lvl_debug) return level_attr->u.num; dbg(lvl_error, "Invalid debug level in config: %ld", level_attr->u.num); } return lvl_unset; } struct debug * debug_new(struct attr *parent, struct attr **attrs) { struct attr *name,*dbg_level_attr,*level_attr; dbg_level level; name=attr_search(attrs, attr_name); dbg_level_attr=attr_search(attrs, attr_dbg_level); level_attr=attr_search(attrs, attr_level); level = parse_dbg_level(dbg_level_attr,level_attr); #ifdef HAVE_SOCKET if (!name && level==lvl_unset) { struct attr *socket_attr=attr_search(attrs, attr_socket); char *p,*s; if (!socket_attr) return NULL; s=g_strdup(socket_attr->u.str); p=strchr(s,':'); if (!p) { g_free(s); return NULL; } *p++='\0'; debug_sin.sin_family=AF_INET; if (!inet_aton(s, &debug_sin.sin_addr)) { g_free(s); return NULL; } debug_sin.sin_port=ntohs(atoi(p)); if (debug_socket == -1) debug_socket=socket(PF_INET, SOCK_DGRAM, 0); g_free(s); return (struct debug *)&dummy; } #endif if (!name || level==lvl_unset) return NULL; debug_level_set(name->u.str, level); return (struct debug *)&dummy; } dbg_level debug_level_get(const char *message_category) { if (!debug_hash) return DEFAULT_DEBUG_LEVEL; gpointer level = g_hash_table_lookup(debug_hash, message_category); if (!level) { return DEFAULT_DEBUG_LEVEL; } return GPOINTER_TO_INT(level); } /** * @brief Write a timestamp to a string buffer * * Timestamp has the format "HH:MM:SS:mmm|" (with mmm=milliseconds), or under Windows "SSSSS:uuuuuu|" (with uuuuuu=microseconds) * * @param[out] buffer The buffer to write to * * @warning Buffer overflow may occur on @p buffer, if it is less than 14-bytes long (13 chars will be stored at max) */ static void debug_timestamp(char *buffer) { #if defined HAVE_API_WIN32_CE || defined _MSC_VER LARGE_INTEGER counter, frequency; double val; unsigned int intpart; QueryPerformanceCounter(&counter); QueryPerformanceFrequency(&frequency); val=counter.HighPart * 4294967296.0 + counter.LowPart; val/=frequency.HighPart * 4294967296.0 + frequency.LowPart; intpart=((unsigned int)val)/100000; /* Extract all digits above 5 lower from integer part */ val = val - (intpart * 100000); /* Limit val integer part to 5 digits */ sprintf(buffer,"%.6f|",val); #else struct timeval tv; if (gettimeofday(&tv, NULL) == -1) return; /* Timestamps are UTC */ sprintf(buffer, "%02d:%02d:%02d.%03d|", (int)(tv.tv_sec/3600)%24, (int)(tv.tv_sec/60)%60, (int)tv.tv_sec % 60, (int)tv.tv_usec/1000); #endif } static char* dbg_level_to_string(dbg_level level) { switch(level) { case lvl_unset: return "-unset-"; case lvl_error: return "error"; case lvl_warning: return "warning"; case lvl_info: return "info"; case lvl_debug: return "debug"; } return "-invalid level-"; } #ifdef HAVE_API_ANDROID static android_LogPriority dbg_level_to_android(dbg_level level) { switch(level) { case lvl_unset: return ANDROID_LOG_UNKNOWN; case lvl_error: return ANDROID_LOG_ERROR; case lvl_warning: return ANDROID_LOG_WARN; case lvl_info: return ANDROID_LOG_INFO; case lvl_debug: return ANDROID_LOG_DEBUG; } return ANDROID_LOG_UNKNOWN; } #endif /** * @brief Write a log message * * @param level The level of the message. The message will only be written if \p level is higher than the minimum (global, per module or per function) * @param module The name of the module that is initiating the log message * @param mlen The length of string \p module * @param function The name of the function that is initiating the log message * @param flen The length of string \p function * @param prefix Force prepending the message with context information (a timestamp, if timestamp_prefix is set), and the module and function name * @param fmt The format string that specifies how subsequent arguments are output * @param ap A list of arguments to use for substitution in the format string */ void debug_vprintf(dbg_level level, const char *module, const int mlen, const char *function, const int flen, int prefix, const char *fmt, va_list ap) { char *end; /* Pointer to the NUL terminating byte of debug_message */ char debug_message[4096]; char *message_origin = debug_message + sizeof(debug_message) -1; /* message_origin is actually stored at the very end of debug_message buffer */ size_t len; /* Length of the currently processed C-string */ /* Here we store a description of the source of the debugging message (message_origin) * For this, we use the last bytes of the debug_message[] buffer. * For example, if message_origin is "gui_internal:gui_internal_set_attr", debug_message[] will contain: * "gui_internal:gui_internal_set_attr\0" with '\0' being the last byte (stored in debug_message[sizeof(debug_message) -1]) */ *message_origin = '\0'; /* Force string termination of message_origin (last byte of debug_message) */ #if defined HAVE_API_WIN32_CE || defined _MSC_VER len = strlen(function); #else len = flen; #endif message_origin -= len; dbg_assert(message_origin >= debug_message); memmove(message_origin, function, len); message_origin--; dbg_assert(message_origin >= debug_message); *message_origin = ':'; #if defined HAVE_API_WIN32_CE || defined _MSC_VER len = strlen(module); #else len = mlen; #endif message_origin -= len; dbg_assert(message_origin >= debug_message); memmove(message_origin, module, len); /* The source of the debug message has been created, is terminated with '\0' and stored at the very end of the debug_message buffer. */ if (global_debug_level >= level || debug_level_get(module) >= level || debug_level_get(message_origin) >= level) { /* Do we output a debug message, based on the current debug level set */ #if defined(DEBUG_WIN32_CE_MESSAGEBOX) wchar_t muni[4096]; #endif debug_message[0]='\0'; end = debug_message; if (prefix) { if (timestamp_prefix) { /* Do we prepend with a timestamp? */ dbg_assert(sizeof(debug_message)>=14); debug_timestamp(debug_message); len = strlen(debug_message); end = debug_message+len; } /* When we reach this part of the code, end is a pointer to the beginning of the debug message (inside the buffer debug_message) */ g_strlcpy(end, dbg_level_to_string(level), sizeof(debug_message) - (end - debug_message)); /* Add the debug level for the current debug message level */ len = strlen(debug_message); end = debug_message+len; /* Have len points to the end of the constructed string */ dbg_assert(end < debug_message+sizeof(debug_message)); /* Make sure we don't get any overflow */ *end++ = ':'; /* In the code below, we add the message_origin to the debug message */ len=strlen(message_origin); dbg_assert(end+len < debug_message+sizeof(debug_message)); /* Make sure we don't get any overflow */ memmove(end,message_origin,len); /* We use memmove here as both message_origin and destination may overlap */ message_origin = NULL; /* Warning: from this point, we must not use the pointer message_origin as the content of the debug_message may be overwritten at any time. */ end+=len; dbg_assert(end+1 < debug_message+sizeof( debug_message)); /* Make sure we don't get any overflow for both ':' and terminating '\0' */ *end++ = ':'; *end = '\0'; /* Force termination of the string */ /* When we get here, debug_message contains: * "ttttttttttttt|error:gui_internal:gui_internal_set_attr:\0" (if timestamps are enabled) or * "error:gui_internal:gui_internal_set_attr:\0" otherwise. * end points to the terminating '\0' */ } #if defined HAVE_API_WIN32_CE #define vsnprintf _vsnprintf #endif len = strlen(debug_message); vsnprintf(end,sizeof(debug_message) - len,fmt, ap); /* Concatenate the debug log message itself to the prefix constructed above */ len = strlen(debug_message); /* Adjust len to store the length of the current string */ end = debug_message+len; /* Adjust end to point to the terminating '\0' of the current string */ /* In the code below, we prepend the end-of-line sequence to the current string pointed by debug_message ("\r\n" for Windows, "\r" otherwise */ #ifdef HAVE_API_WIN32_BASE if (len + 1 < sizeof(debug_message) - 1) { /* For Windows platforms, add \r at the end of the buffer (if any room), make sure that we have room for one more character */ *end++ = '\r'; len++; *end = '\0'; } #endif if (len + 1 < sizeof(debug_message)) { /* Add \n at the end of the buffer (if any room) */ *end++ = '\n'; len++; *end = '\0'; } #ifdef DEBUG_WIN32_CE_MESSAGEBOX mbstowcs(muni, debug_message, len+1); MessageBoxW(NULL, muni, TEXT("Navit - Error"), MB_APPLMODAL|MB_OK|MB_ICONERROR); #else #ifdef HAVE_API_ANDROID __android_log_print(dbg_level_to_android(level), "navit", "%s", debug_message); #else #ifdef HAVE_SOCKET if (debug_socket != -1) { sendto(debug_socket, debug_message, len, 0, (struct sockaddr *)&debug_sin, sizeof(debug_sin)); return; } #endif FILE *fp=debug_fp; if (! fp) fp = stderr; fprintf(fp,"%s",debug_message); fflush(fp); #endif #endif } } void debug_printf(dbg_level level, const char *module, const int mlen,const char *function, const int flen, int prefix, const char *fmt, ...) { va_list ap; va_start(ap, fmt); debug_vprintf(level, module, mlen, function, flen, prefix, fmt, ap); va_end(ap); } void debug_assert_fail(const char *module, const int mlen,const char *function, const int flen, const char *file, int line, const char *expr) { debug_printf(lvl_error,module,mlen,function,flen,1,"%s:%d assertion failed:%s\n", file, line, expr); abort(); } void debug_destroy(void) { if (!debug_fp) return; if (debug_fp == stderr || debug_fp == stdout) return; fclose(debug_fp); debug_fp = NULL; } void debug_set_logfile(const char *path) { FILE *fp; fp = fopen(path, "a"); if (fp) { debug_destroy(); debug_fp = fp; fprintf(debug_fp, "Navit log started\n"); fflush(debug_fp); } } struct malloc_head { int magic; int size; char *where; void *return_address[8]; struct malloc_head *prev; struct malloc_head *next; } *malloc_heads; struct malloc_tail { int magic; }; int mallocs,debug_malloc_size,debug_malloc_size_m; void debug_dump_mallocs(void) { struct malloc_head *head=malloc_heads; int i; dbg(lvl_debug,"mallocs %d",mallocs); while (head) { fprintf(stderr,"unfreed malloc from %s of size %d\n",head->where,head->size); for (i = 0 ; i < 8 ; i++) fprintf(stderr,"\tlist *%p\n",head->return_address[i]); head=head->next; } } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wframe-address" // We know what we are doing here, suppress warning void *debug_malloc(const char *where, int line, const char *func, int size) { struct malloc_head *head; struct malloc_tail *tail; if (!size) return NULL; mallocs++; debug_malloc_size+=size; if (debug_malloc_size/(1024*1024) != debug_malloc_size_m) { debug_malloc_size_m=debug_malloc_size/(1024*1024); dbg(lvl_debug,"malloced %d kb",debug_malloc_size/1024); } head=malloc(size+sizeof(*head)+sizeof(*tail)); head->magic=0xdeadbeef; head->size=size; head->prev=NULL; head->next=malloc_heads; malloc_heads=head; if (head->next) head->next->prev=head; head->where=g_strdup_printf("%s:%d %s",where,line,func); #if !defined (__GNUC__) #define __builtin_return_address(x) NULL #endif head->return_address[0]=__builtin_return_address(0); head->return_address[1]=__builtin_return_address(1); head->return_address[2]=__builtin_return_address(2); head->return_address[3]=__builtin_return_address(3); head->return_address[4]=__builtin_return_address(4); head->return_address[5]=__builtin_return_address(5); head->return_address[6]=__builtin_return_address(6); head->return_address[7]=__builtin_return_address(7); head++; tail=(struct malloc_tail *)((unsigned char *)head+size); tail->magic=0xdeadbef0; return head; } #pragma GCC diagnostic pop void *debug_malloc0(const char *where, int line, const char *func, int size) { void *ret=debug_malloc(where, line, func, size); if (ret) memset(ret, 0, size); return ret; } void *debug_realloc(const char *where, int line, const char *func, void *ptr, int size) { void *ret=debug_malloc(where, line, func, size); if (ret && ptr) memcpy(ret, ptr, size); debug_free(where, line, func, ptr); return ret; } char *debug_strdup(const char *where, int line, const char *func, const char *ptr) { int size; char *ret; if (!ptr) return NULL; size=strlen(ptr)+1; ret=debug_malloc(where, line, func, size); memcpy(ret, ptr, size); return ret; } char *debug_guard(const char *where, int line, const char *func, char *str) { char *ret=debug_strdup(where, line, func, str); g_free(str); return ret; } void debug_free(const char *where, int line, const char *func, void *ptr) { struct malloc_head *head; struct malloc_tail *tail; if (!ptr) return; mallocs--; head=(struct malloc_head *)((unsigned char *)ptr-sizeof(*head)); tail=(struct malloc_tail *)((unsigned char *)ptr+head->size); debug_malloc_size-=head->size; if (head->magic != 0xdeadbeef || tail->magic != 0xdeadbef0) { fprintf(stderr,"Invalid free from %s:%d %s\n",where,line,func); } head->magic=0; tail->magic=0; if (head->prev) head->prev->next=head->next; else malloc_heads=head->next; if (head->next) head->next->prev=head->prev; free(head->where); free(head); } void debug_free_func(void *ptr) { debug_free("unknown",0,"unknown",ptr); } void debug_finished(void) { debug_dump_mallocs(); g_free(gdb_program); g_hash_table_destroy(debug_hash); debug_destroy(); }