Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bctrace.C
1 /*
2  *  CINELERRA
3  * Copyright (C) 2016-2020 William Morrow
4  *
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.
9  *
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.
14  *
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
18  * USA
19  */
20
21
22 #include <stdio.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26
27 #include "bctrace.h"
28
29 #ifdef BOOBY
30 #include <execinfo.h>
31 #define BT_BUF_SIZE 100
32 // booby trap (backtrace)
33 void booby() {
34         printf("BOOBY!\n"); 
35         void *buffer[BT_BUF_SIZE];
36         int nptrs = backtrace(buffer, BT_BUF_SIZE);
37         char **trace = backtrace_symbols(buffer, nptrs);
38         if( !trace ) return;
39         for( int i=0; i<nptrs; ) printf("%s\n", trace[i++]);
40         free(trace);
41 }
42 #endif
43
44 BC_Trace *BC_Trace::global_trace = 0;
45 int trace_memory = 0;
46 int trace_locks = 1;
47
48 BC_Trace::BC_Trace()
49 {
50 }
51 BC_Trace::~BC_Trace()
52 {
53 }
54
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;
60
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;
65
66 trace_item::trace_item(bc_trace_t &t) : table(t) { ++table.size; }
67 trace_item::~trace_item() { --table.size; }
68
69 extern "C" void dump()
70 {
71         BC_Trace::dump_traces();
72         BC_Trace::dump_locks();
73         BC_Trace::dump_buffers();
74 }
75
76 #define TOTAL_TRACES 16
77
78 void BC_Trace::new_trace(const char *text)
79 {
80         if(!global_trace) return;
81         execution_table.lock();
82         execution_item *it;
83         if( execution_table.size >= TOTAL_TRACES ) {
84                 it = (execution_item *)execution_table.first;
85                 execution_table.remove_pointer(it);
86         }
87         else
88                 it = new execution_item();
89         it->set(text);
90         execution_table.append(it);
91         execution_table.unlock();
92 }
93
94 void BC_Trace::new_trace(const char *file, const char *function, int line)
95 {
96         char string[BCTEXTLEN];
97         snprintf(string, BCTEXTLEN, "%s: %s: %d", file, function, line);
98         new_trace(string);
99 }
100
101 void BC_Trace::delete_traces()
102 {
103         if(!global_trace) return;
104         execution_table.lock();
105         execution_table.clear();
106         execution_table.unlock();
107 }
108
109 void BC_Trace::enable_locks()
110 {
111         lock_table.lock();
112         trace_locks = 1;
113         lock_table.unlock();
114 }
115
116 void BC_Trace::disable_locks()
117 {
118         lock_table.lock();
119         trace_locks = 0;
120         while( lock_table.last ) {
121                 lock_item *p = (lock_item*)lock_table.last;
122                 lock_table.remove_pointer(p);
123                 p->info->trace = 0;
124                 lock_free.append(p);
125         }
126         lock_free.clear();
127         lock_table.unlock();
128 }
129
130 // no canceling with lock held
131 void BC_Trace::lock_locks(const char *s)
132 {
133         lock_table.lock();
134         last_lock_thread = pthread_self();
135         last_lock_title = s;
136         last_lock_location = 0;
137 }
138
139 void BC_Trace::unlock_locks()
140 {
141         lock_table.unlock();
142 }
143
144 #define TOTAL_LOCKS 256
145
146 int BC_Trace::set_lock(const char *title, const char *loc, trace_info *info)
147 {
148         if( !global_trace || !trace_locks ) return 0;
149         lock_table.lock();
150         last_lock_thread = pthread_self();
151         last_lock_title = title;
152         last_lock_location = loc;
153         lock_item *it;
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);
159         }
160         else
161                 it = new lock_item();
162         it->set(info, title, loc);
163         lock_table.append(it);
164         info->trace = (void *)it;
165         lock_table.unlock();
166         return it->id;
167 }
168
169 void BC_Trace::set_lock2(int table_id, trace_info *info)
170 {
171         if( !global_trace || !trace_locks ) return;
172         lock_table.lock();
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;
177         }
178         if( p ) {
179                 info->trace = (void *)p;
180                 p->is_owner = 1;
181                 p->tid = pthread_self();
182         }
183         lock_table.unlock();
184 }
185
186 void BC_Trace::unset_lock2(int table_id, trace_info *info)
187 {
188         if( !global_trace || !trace_locks ) return;
189         lock_table.lock();
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;
194         }
195         if( p ) {
196                 if( p->list == &lock_table ) {
197                         lock_table.remove_pointer(p);
198                         info->trace = 0;
199                         lock_free.append(p);
200                 }
201                 else
202                         printf("unset_lock2: free lock_item in lock_table %p\n", p);
203         }
204         lock_table.unlock();
205 }
206
207 void BC_Trace::unset_lock(trace_info *info)
208 {
209         if( !global_trace || !trace_locks ) return;
210         lock_table.lock();
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;
215         }
216         info->trace = 0;
217         if( p ) {
218                 if( p->list == &lock_table ) {
219                         lock_table.remove_pointer(p);
220                         info->trace = 0;
221                         lock_free.append(p);
222                 }
223                 else
224                         printf("unset_lock: free lock_item in lock_table %p\n", p);
225         }
226         lock_table.unlock();
227 }
228
229
230 void BC_Trace::unset_all_locks(trace_info *info)
231 {
232         if( !global_trace || !trace_locks ) return;
233         lock_table.lock();
234         info->trace = 0;
235         lock_item *p = (lock_item*)lock_table.first;
236         while( p ) {
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);
241         }
242         lock_table.unlock();
243 }
244
245 void BC_Trace::clear_locks_tid(pthread_t tid)
246 {
247         if( !global_trace || !trace_locks ) return;
248         lock_table.lock();
249         lock_item *p = (lock_item*)lock_table.first;
250         while( p ) {
251                 lock_item *lp = p;  p = (lock_item*)p->next;
252                 if( lp->tid != tid ) continue;
253                 lock_table.remove_pointer(lp);
254                 lp->info->trace = 0;
255                 lock_free.append(lp);
256         }
257         lock_table.unlock();
258 }
259
260
261 void BC_Trace::enable_memory()
262 {
263         trace_memory = 1;
264 }
265
266 void BC_Trace::disable_memory()
267 {
268         trace_memory = 0;
269 }
270
271
272 void BC_Trace::set_buffer(int size, void *ptr, const char* loc)
273 {
274         if(!global_trace) return;
275         if(!trace_memory) return;
276         memory_table.lock();
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();
280 }
281
282 int BC_Trace::unset_buffer(void *ptr)
283 {
284         if(!global_trace) return 0;
285         if(!trace_memory) return 0;
286         memory_table.lock();
287         memory_item *p = (memory_item*)memory_table.first;
288         for( ; p!=0 && p->ptr!=ptr; p=(memory_item*)p->next );
289         if( p ) delete p;
290         memory_table.unlock();
291         return !p ? 1 : 0;
292 }
293
294 #ifdef TRACE_MEMORY
295
296 void* operator new(size_t size)
297 {
298         void *result = malloc(size);
299         BUFFER(size, result, "new");
300         return result;
301 }
302
303 void* operator new[](size_t size)
304 {
305         void *result = malloc(size);
306         BUFFER(size, result, "new []");
307         return result;
308 }
309
310 void operator delete(void *ptr)
311 {
312         UNBUFFER(ptr);
313         free(ptr);
314 }
315
316 void operator delete[](void *ptr)
317 {
318         UNBUFFER(ptr);
319         free(ptr);
320 }
321
322 #endif
323
324 void BC_Trace::dump_traces(FILE *fp)
325 {
326 // Dump trace table
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);
330         }
331 }
332
333 void trace_info::set_owner() { owner = (unsigned long)pthread_self(); }
334 void trace_info::unset_owner() { owner = 0; }
335
336 void BC_Trace::dump_locks(FILE *fp)
337 {
338 // Dump lock table
339 #ifdef TRACE_LOCKS
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);
348                 fprintf(fp,"\n");
349         }
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);
355         if( missed_locks )
356                 printf("miss locks: %jd\n", missed_locks);
357 #endif
358 }
359
360 void BC_Trace::dump_buffers(FILE *fp)
361 {
362 #ifdef TRACE_MEMORY
363         memory_table.lock();
364 // Dump buffer table
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);
369         }
370         memory_table.unlock();
371 #endif
372 }
373
374 void BC_Trace::delete_temps()
375 {
376         file_table.lock();
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);
382                 ::remove(p->value);
383                 delete p;
384         }
385         file_table.unlock();
386 }
387
388 void BC_Trace::reset_locks()
389 {
390         lock_table.unlock();
391 }
392
393 void BC_Trace::set_temp(char *string)
394 {
395         file_table.lock();
396         file_item *it = new file_item();
397         it->set(string);
398         file_table.append(it);
399         file_table.unlock();
400 }
401
402 void BC_Trace::unset_temp(char *string)
403 {
404         file_table.lock();
405         file_item *p = (file_item *)file_table.last;
406         for( ; p!=0 && strcmp(p->value,string); p=(file_item*)p->previous );
407         if( p ) delete p;
408         file_table.unlock();
409 }
410
411
412 #ifdef TRACE_THREADS
413
414 TheLock TheLocker::the_lock;
415 TheList TheList::the_list;
416
417 int lock_item::table_id = 0;
418
419 void TheList::dbg_add(pthread_t tid, pthread_t owner, const char *nm)
420 {
421         TheLocker the_locker;
422         int i = the_list.size();
423         while( --i >= 0 && !(the_list[i]->tid == tid && the_list[i]->owner == owner) );
424         if( i >= 0 ) {
425                 printf("dbg_add, dup %016lx %s %s\n",
426                         (unsigned long)tid, nm, the_list[i]->name);
427                 return;
428         }
429         the_list.append(new TheDbg(tid, owner, nm));
430 }
431
432 void TheList::dbg_del(pthread_t tid)
433 {
434         TheLocker the_locker;
435         int i = the_list.size();
436         while( --i >= 0 && the_list[i]->tid != tid );
437         if( i < 0 ) {
438                 printf("dbg_del, mis %016lx\n",(unsigned long)tid);
439                 return;
440         }
441         the_list.remove_object_number(i);
442 }
443
444
445 void TheList::dump_threads(FILE *fp)
446 {
447         int i = the_list.size();
448         while( --i >= 0 ) {
449                 fprintf(fp, "thread 0x%012lx, owner 0x%012lx, %s\n",
450                         (unsigned long)the_list[i]->tid, (unsigned long)the_list[i]->owner,
451                         the_list[i]->name);
452         }
453 }
454
455 #else
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)
459 {
460 }
461 #endif
462
463 void BC_Trace::dump_threads(FILE *fp)
464 {
465         TheList::dump_threads(fp);
466 }
467
468
469 void BC_Trace::dump_shm_stat(const char *fn, FILE *fp)
470 {
471         char path[BCTEXTLEN];
472         sprintf(path, "/proc/sys/kernel/%s",fn);
473         FILE *sfp = fopen(path,"r");
474         if( !sfp ) return;
475         uint64_t v = 0;
476         fscanf(sfp, "%ju", &v);
477         fclose(sfp);
478         fprintf(fp, "%s = %ju\n", fn, v);
479 }
480
481 void BC_Trace::dump_shm_stats(FILE *fp)
482 {
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");
487         if( !sfp ) return;
488         char line[BCTEXTLEN];
489         int pid = getpid();
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;
496                 if( sscanf(line,
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;
501                 if( cpid == pid ) {
502                         used += size;
503                         ++n_used;
504                 }
505                 else {
506                         other += size;
507                         ++n_other;
508                 }
509         }
510         fclose(sfp);
511         fprintf(fp, "shmused = %jd (%d items)\n", used, n_used);
512         fprintf(fp, "shmother = %jd (%d items)\n", other, n_other);
513 }
514