3 * Copyright (C) 2016-2020 William Morrow
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published
7 * by the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
31 #define BT_BUF_SIZE 100
32 // booby trap (backtrace)
35 void *buffer[BT_BUF_SIZE];
36 int nptrs = backtrace(buffer, BT_BUF_SIZE);
37 char **trace = backtrace_symbols(buffer, nptrs);
39 for( int i=0; i<nptrs; ) printf("%s\n", trace[i++]);
44 BC_Trace *BC_Trace::global_trace = 0;
55 bc_trace_mutex execution_table;
56 bc_trace_mutex memory_table;
57 bc_trace_mutex lock_table;
58 bc_trace_list lock_free;
59 bc_trace_mutex file_table;
61 // incase lock set after task ends
62 static pthread_t last_lock_thread = 0;
63 static const char *last_lock_title = 0;
64 static const char *last_lock_location = 0;
66 trace_item::trace_item(bc_trace_t &t) : table(t) { ++table.size; }
67 trace_item::~trace_item() { --table.size; }
69 extern "C" void dump()
71 BC_Trace::dump_traces();
72 BC_Trace::dump_locks();
73 BC_Trace::dump_buffers();
76 #define TOTAL_TRACES 16
78 void BC_Trace::new_trace(const char *text)
80 if(!global_trace) return;
81 execution_table.lock();
83 if( execution_table.size >= TOTAL_TRACES ) {
84 it = (execution_item *)execution_table.first;
85 execution_table.remove_pointer(it);
88 it = new execution_item();
90 execution_table.append(it);
91 execution_table.unlock();
94 void BC_Trace::new_trace(const char *file, const char *function, int line)
96 char string[BCTEXTLEN];
97 snprintf(string, BCTEXTLEN, "%s: %s: %d", file, function, line);
101 void BC_Trace::delete_traces()
103 if(!global_trace) return;
104 execution_table.lock();
105 execution_table.clear();
106 execution_table.unlock();
109 void BC_Trace::enable_locks()
116 void BC_Trace::disable_locks()
120 while( lock_table.last ) {
121 lock_item *p = (lock_item*)lock_table.last;
122 lock_table.remove_pointer(p);
130 // no canceling with lock held
131 void BC_Trace::lock_locks(const char *s)
134 last_lock_thread = pthread_self();
136 last_lock_location = 0;
139 void BC_Trace::unlock_locks()
144 #define TOTAL_LOCKS 256
146 int BC_Trace::set_lock(const char *title, const char *loc, trace_info *info)
148 if( !global_trace || !trace_locks ) return 0;
150 last_lock_thread = pthread_self();
151 last_lock_title = title;
152 last_lock_location = loc;
154 if( (it=(lock_item*)lock_free.first) != 0 )
155 lock_free.remove_pointer(it);
156 else if( lock_table.size >= TOTAL_LOCKS ) {
157 it = (lock_item*)lock_table.first;
158 lock_table.remove_pointer(it);
161 it = new lock_item();
162 it->set(info, title, loc);
163 lock_table.append(it);
164 info->trace = (void *)it;
169 void BC_Trace::set_lock2(int table_id, trace_info *info)
171 if( !global_trace || !trace_locks ) return;
173 lock_item *p = (lock_item *)info->trace;
174 if( !p || p->id != table_id ) {
175 p = (lock_item*)lock_table.last;
176 while( p && p->id != table_id ) p = (lock_item*)p->previous;
179 info->trace = (void *)p;
181 p->tid = pthread_self();
186 void BC_Trace::unset_lock2(int table_id, trace_info *info)
188 if( !global_trace || !trace_locks ) return;
190 lock_item *p = (lock_item *)info->trace;
191 if( !p || p->id != table_id ) {
192 p = (lock_item*)lock_table.last;
193 while( p && p->id != table_id ) p = (lock_item*)p->previous;
196 if( p->list == &lock_table ) {
197 lock_table.remove_pointer(p);
202 printf("unset_lock2: free lock_item in lock_table %p\n", p);
207 void BC_Trace::unset_lock(trace_info *info)
209 if( !global_trace || !trace_locks ) return;
211 lock_item *p = (lock_item *)info->trace;
212 if( !p || p->info!=info || !p->is_owner ) {
213 p = (lock_item*)lock_table.last;
214 while( p && ( p->info!=info || !p->is_owner ) ) p = (lock_item*)p->previous;
218 if( p->list == &lock_table ) {
219 lock_table.remove_pointer(p);
224 printf("unset_lock: free lock_item in lock_table %p\n", p);
230 void BC_Trace::unset_all_locks(trace_info *info)
232 if( !global_trace || !trace_locks ) return;
235 lock_item *p = (lock_item*)lock_table.first;
237 lock_item *lp = p; p = (lock_item*)p->next;
238 if( lp->info != info ) continue;
239 lock_table.remove_pointer(lp);
240 lock_free.append(lp);
245 void BC_Trace::clear_locks_tid(pthread_t tid)
247 if( !global_trace || !trace_locks ) return;
249 lock_item *p = (lock_item*)lock_table.first;
251 lock_item *lp = p; p = (lock_item*)p->next;
252 if( lp->tid != tid ) continue;
253 lock_table.remove_pointer(lp);
255 lock_free.append(lp);
261 void BC_Trace::enable_memory()
266 void BC_Trace::disable_memory()
272 void BC_Trace::set_buffer(int size, void *ptr, const char* loc)
274 if(!global_trace) return;
275 if(!trace_memory) return;
277 //printf("BC_Trace::set_buffer %p %s\n", ptr, loc);
278 memory_table.append(new memory_item(size, ptr, loc));
279 memory_table.unlock();
282 int BC_Trace::unset_buffer(void *ptr)
284 if(!global_trace) return 0;
285 if(!trace_memory) return 0;
287 memory_item *p = (memory_item*)memory_table.first;
288 for( ; p!=0 && p->ptr!=ptr; p=(memory_item*)p->next );
290 memory_table.unlock();
296 void* operator new(size_t size)
298 void *result = malloc(size);
299 BUFFER(size, result, "new");
303 void* operator new[](size_t size)
305 void *result = malloc(size);
306 BUFFER(size, result, "new []");
310 void operator delete(void *ptr)
316 void operator delete[](void *ptr)
324 void BC_Trace::dump_traces(FILE *fp)
327 for( trace_item *tp=execution_table.first; tp!=0; tp=tp->next ) {
328 execution_item *p=(execution_item*)tp;
329 fprintf(fp," %s\n", (char*)p->value);
333 void trace_info::set_owner() { owner = (unsigned long)pthread_self(); }
334 void trace_info::unset_owner() { owner = 0; }
336 void BC_Trace::dump_locks(FILE *fp)
340 fprintf(fp,"signal_entry: lock table size=%d\n", lock_table.size);
341 for( trace_item *tp=lock_table.first; tp!=0; tp=tp->next ) {
342 lock_item *p=(lock_item*)tp;
343 fprintf(fp," %p %s, %s %p%s",
344 p->info, p->title, p->loc,
345 (void*)p->tid, p->is_owner ? " *" : "");
346 if( p->info->owner && p->info->owner != (unsigned long)p->tid )
347 fprintf(fp," %p", (void*)p->info->owner);
350 int64_t lock_total = lock_table.total();
351 int64_t free_total = lock_free.total();
352 printf("lock_items: %jd\n", lock_total);
353 printf("lock_frees: %jd\n", free_total);
354 int64_t missed_locks = lock_table.size - (lock_total + free_total);
356 printf("miss locks: %jd\n", missed_locks);
360 void BC_Trace::dump_buffers(FILE *fp)
365 fprintf(fp,"BC_Trace::dump_buffers: buffer table size=%d\n", memory_table.size);
366 for( trace_item *tp=memory_table.first; tp!=0; tp=tp->next ) {
367 memory_item *p=(memory_item*)tp;
368 fprintf(fp," %d %p %s\n", p->size, p->ptr, p->loc);
370 memory_table.unlock();
374 void BC_Trace::delete_temps()
377 if( file_table.size )
378 printf("BC_Trace::delete_temps: deleting %d temp files\n", file_table.size);
379 while( file_table.first ) {
380 file_item *p = (file_item*)file_table.first;
381 printf(" %s\n", p->value);
388 void BC_Trace::reset_locks()
393 void BC_Trace::set_temp(char *string)
396 file_item *it = new file_item();
398 file_table.append(it);
402 void BC_Trace::unset_temp(char *string)
405 file_item *p = (file_item *)file_table.last;
406 for( ; p!=0 && strcmp(p->value,string); p=(file_item*)p->previous );
414 TheLock TheLocker::the_lock;
415 TheList TheList::the_list;
417 int lock_item::table_id = 0;
419 void TheList::dbg_add(pthread_t tid, pthread_t owner, const char *nm)
421 TheLocker the_locker;
422 int i = the_list.size();
423 while( --i >= 0 && !(the_list[i]->tid == tid && the_list[i]->owner == owner) );
425 printf("dbg_add, dup %016lx %s %s\n",
426 (unsigned long)tid, nm, the_list[i]->name);
429 the_list.append(new TheDbg(tid, owner, nm));
432 void TheList::dbg_del(pthread_t tid)
434 TheLocker the_locker;
435 int i = the_list.size();
436 while( --i >= 0 && the_list[i]->tid != tid );
438 printf("dbg_del, mis %016lx\n",(unsigned long)tid);
441 the_list.remove_object_number(i);
445 void TheList::dump_threads(FILE *fp)
447 int i = the_list.size();
449 fprintf(fp, "thread 0x%012lx, owner 0x%012lx, %s\n",
450 (unsigned long)the_list[i]->tid, (unsigned long)the_list[i]->owner,
456 #define dbg_add(t, o, nm) do {} while(0)
457 #define dbg_del(t) do {} while(0)
458 void TheList::dump_threads(FILE *fp)
463 void BC_Trace::dump_threads(FILE *fp)
465 TheList::dump_threads(fp);
469 void BC_Trace::dump_shm_stat(const char *fn, FILE *fp)
471 char path[BCTEXTLEN];
472 sprintf(path, "/proc/sys/kernel/%s",fn);
473 FILE *sfp = fopen(path,"r");
476 fscanf(sfp, "%ju", &v);
478 fprintf(fp, "%s = %ju\n", fn, v);
481 void BC_Trace::dump_shm_stats(FILE *fp)
483 dump_shm_stat("shmall", fp);
484 dump_shm_stat("shmmax", fp);
485 dump_shm_stat("shmmni", fp);
486 FILE *sfp = fopen("/proc/sysvipc/shm","r");
488 char line[BCTEXTLEN];
490 if( !fgets(line,sizeof(line), sfp) ) return;
491 int64_t used = 0, other = 0;
492 int n_used = 0, n_other = 0;
493 while( fgets(line,sizeof(line), sfp) ) {
494 int key, shmid, perms, cpid, lpid, uid, gid, cuid, cgid;
495 int64_t size, nattch, atime, dtime, ctime, rss, swap;
497 "%d %d %o %ju %u %u %ju %u %u %u %u %ju %ju %ju %ju %ju",
498 &key, &shmid, &perms, &size, &cpid, &lpid, &nattch,
499 &uid, &gid, &cuid, &cgid, &atime, &dtime, &ctime,
500 &rss, &swap) != 16 ) break;
511 fprintf(fp, "shmused = %jd (%d items)\n", used, n_used);
512 fprintf(fp, "shmother = %jd (%d items)\n", other, n_other);