es.po update by rafa, add ms win10 cygwin port, add pulseaudio, new config flags...
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bctrace.C
1 #include <stdio.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5
6 #include "bctrace.h"
7
8 #ifdef BOOBY
9 #include <execinfo.h>
10 #define BT_BUF_SIZE 100
11 // booby trap (backtrace)
12 void booby() {
13         printf("BOOBY!\n"); 
14         void *buffer[BT_BUF_SIZE];
15         int nptrs = backtrace(buffer, BT_BUF_SIZE);
16         char **trace = backtrace_symbols(buffer, nptrs);
17         if( !trace ) return;
18         for( int i=0; i<nptrs; ) printf("%s\n", trace[i++]);
19         free(trace);
20 }
21 #endif
22
23 BC_Trace *BC_Trace::global_trace = 0;
24 int trace_memory = 0;
25 int trace_locks = 1;
26
27 BC_Trace::BC_Trace()
28 {
29 }
30 BC_Trace::~BC_Trace()
31 {
32 }
33
34 bc_trace_mutex execution_table;
35 bc_trace_mutex memory_table;
36 bc_trace_mutex lock_table;
37 bc_trace_list  lock_free;
38 bc_trace_mutex file_table;
39
40 // incase lock set after task ends
41 static pthread_t last_lock_thread = 0;
42 static const char *last_lock_title = 0;
43 static const char *last_lock_location = 0;
44
45 trace_item::trace_item(bc_trace_t &t) : table(t) { ++table.size; }
46 trace_item::~trace_item() { --table.size; }
47
48 extern "C" void dump()
49 {
50         BC_Trace::dump_traces();
51         BC_Trace::dump_locks();
52         BC_Trace::dump_buffers();
53 }
54
55 #define TOTAL_TRACES 16
56
57 void BC_Trace::new_trace(const char *text)
58 {
59         if(!global_trace) return;
60         execution_table.lock();
61         execution_item *it;
62         if( execution_table.size >= TOTAL_TRACES ) {
63                 it = (execution_item *)execution_table.first;
64                 execution_table.remove_pointer(it);
65         }
66         else
67                 it = new execution_item();
68         it->set(text);
69         execution_table.append(it);
70         execution_table.unlock();
71 }
72
73 void BC_Trace::new_trace(const char *file, const char *function, int line)
74 {
75         char string[BCTEXTLEN];
76         snprintf(string, BCTEXTLEN, "%s: %s: %d", file, function, line);
77         new_trace(string);
78 }
79
80 void BC_Trace::delete_traces()
81 {
82         if(!global_trace) return;
83         execution_table.lock();
84         execution_table.clear();
85         execution_table.unlock();
86 }
87
88 void BC_Trace::enable_locks()
89 {
90         lock_table.lock();
91         trace_locks = 1;
92         lock_table.unlock();
93 }
94
95 void BC_Trace::disable_locks()
96 {
97         lock_table.lock();
98         trace_locks = 0;
99         while( lock_table.last ) {
100                 lock_item *p = (lock_item*)lock_table.last;
101                 lock_table.remove_pointer(p);
102                 p->info->trace = 0;
103                 lock_free.append(p);
104         }
105         lock_free.clear();
106         lock_table.unlock();
107 }
108
109 // no canceling with lock held
110 void BC_Trace::lock_locks(const char *s)
111 {
112         lock_table.lock();
113         last_lock_thread = pthread_self();
114         last_lock_title = s;
115         last_lock_location = 0;
116 }
117
118 void BC_Trace::unlock_locks()
119 {
120         lock_table.unlock();
121 }
122
123 #define TOTAL_LOCKS 256
124
125 int BC_Trace::set_lock(const char *title, const char *loc, trace_info *info)
126 {
127         if( !global_trace || !trace_locks ) return 0;
128         lock_table.lock();
129         last_lock_thread = pthread_self();
130         last_lock_title = title;
131         last_lock_location = loc;
132         lock_item *it;
133         if( (it=(lock_item*)lock_free.first) != 0 )
134                 lock_free.remove_pointer(it);
135         else if( lock_table.size >= TOTAL_LOCKS ) {
136                 it = (lock_item*)lock_table.first;
137                 lock_table.remove_pointer(it);
138         }
139         else
140                 it = new lock_item();
141         it->set(info, title, loc);
142         lock_table.append(it);
143         info->trace = (void *)it;
144         lock_table.unlock();
145         return it->id;
146 }
147
148 void BC_Trace::set_lock2(int table_id, trace_info *info)
149 {
150         if( !global_trace || !trace_locks ) return;
151         lock_table.lock();
152         lock_item *p = (lock_item *)info->trace;
153         if( !p || p->id != table_id ) {
154                 p = (lock_item*)lock_table.last;
155                 while( p && p->id != table_id ) p = (lock_item*)p->previous;
156         }
157         if( p ) {
158                 info->trace = (void *)p;
159                 p->is_owner = 1;
160                 p->tid = pthread_self();
161         }
162         lock_table.unlock();
163 }
164
165 void BC_Trace::unset_lock2(int table_id, trace_info *info)
166 {
167         if( !global_trace || !trace_locks ) return;
168         lock_table.lock();
169         lock_item *p = (lock_item *)info->trace;
170         if( !p || p->id != table_id ) {
171                 p = (lock_item*)lock_table.last;
172                 while( p && p->id != table_id ) p = (lock_item*)p->previous;
173         }
174         if( p ) {
175                 if( p->list == &lock_table ) {
176                         lock_table.remove_pointer(p);
177                         info->trace = 0;
178                         lock_free.append(p);
179                 }
180                 else
181                         printf("unset_lock2: free lock_item in lock_table %p\n", p);
182         }
183         lock_table.unlock();
184 }
185
186 void BC_Trace::unset_lock(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->info!=info || !p->is_owner ) {
192                 p = (lock_item*)lock_table.last;
193                 while( p && ( p->info!=info || !p->is_owner ) ) p = (lock_item*)p->previous;
194         }
195         info->trace = 0;
196         if( p ) {
197                 if( p->list == &lock_table ) {
198                         lock_table.remove_pointer(p);
199                         info->trace = 0;
200                         lock_free.append(p);
201                 }
202                 else
203                         printf("unset_lock: free lock_item in lock_table %p\n", p);
204         }
205         lock_table.unlock();
206 }
207
208
209 void BC_Trace::unset_all_locks(trace_info *info)
210 {
211         if( !global_trace || !trace_locks ) return;
212         lock_table.lock();
213         info->trace = 0;
214         lock_item *p = (lock_item*)lock_table.first;
215         while( p ) {
216                 lock_item *lp = p;  p = (lock_item*)p->next;
217                 if( lp->info != info ) continue;
218                 lock_table.remove_pointer(lp);
219                 lock_free.append(lp);
220         }
221         lock_table.unlock();
222 }
223
224 void BC_Trace::clear_locks_tid(pthread_t tid)
225 {
226         if( !global_trace || !trace_locks ) return;
227         lock_table.lock();
228         lock_item *p = (lock_item*)lock_table.first;
229         while( p ) {
230                 lock_item *lp = p;  p = (lock_item*)p->next;
231                 if( lp->tid != tid ) continue;
232                 lock_table.remove_pointer(lp);
233                 lp->info->trace = 0;
234                 lock_free.append(lp);
235         }
236         lock_table.unlock();
237 }
238
239
240 void BC_Trace::enable_memory()
241 {
242         trace_memory = 1;
243 }
244
245 void BC_Trace::disable_memory()
246 {
247         trace_memory = 0;
248 }
249
250
251 void BC_Trace::set_buffer(int size, void *ptr, const char* loc)
252 {
253         if(!global_trace) return;
254         if(!trace_memory) return;
255         memory_table.lock();
256 //printf("BC_Trace::set_buffer %p %s\n", ptr, loc);
257         memory_table.append(new memory_item(size, ptr, loc));
258         memory_table.unlock();
259 }
260
261 int BC_Trace::unset_buffer(void *ptr)
262 {
263         if(!global_trace) return 0;
264         if(!trace_memory) return 0;
265         memory_table.lock();
266         memory_item *p = (memory_item*)memory_table.first;
267         for( ; p!=0 && p->ptr!=ptr; p=(memory_item*)p->next );
268         if( p ) delete p;
269         memory_table.unlock();
270         return !p ? 1 : 0;
271 }
272
273 #ifdef TRACE_MEMORY
274
275 void* operator new(size_t size)
276 {
277         void *result = malloc(size);
278         BUFFER(size, result, "new");
279         return result;
280 }
281
282 void* operator new[](size_t size)
283 {
284         void *result = malloc(size);
285         BUFFER(size, result, "new []");
286         return result;
287 }
288
289 void operator delete(void *ptr)
290 {
291         UNBUFFER(ptr);
292         free(ptr);
293 }
294
295 void operator delete[](void *ptr)
296 {
297         UNBUFFER(ptr);
298         free(ptr);
299 }
300
301 #endif
302
303 void BC_Trace::dump_traces(FILE *fp)
304 {
305 // Dump trace table
306         for( trace_item *tp=execution_table.first; tp!=0; tp=tp->next ) {
307                 execution_item *p=(execution_item*)tp;
308                 fprintf(fp,"    %s\n", (char*)p->value);
309         }
310 }
311
312 void trace_info::set_owner() { owner = (unsigned long)pthread_self(); }
313 void trace_info::unset_owner() { owner = 0; }
314
315 void BC_Trace::dump_locks(FILE *fp)
316 {
317 // Dump lock table
318 #ifdef TRACE_LOCKS
319         fprintf(fp,"signal_entry: lock table size=%d\n", lock_table.size);
320         for( trace_item *tp=lock_table.first; tp!=0; tp=tp->next ) {
321                 lock_item *p=(lock_item*)tp;
322                 fprintf(fp,"    %p %s, %s %p%s",
323                         p->info, p->title, p->loc,
324                         (void*)p->tid, p->is_owner ? " *" : "");
325                 if( p->info->owner && p->info->owner != (unsigned long)p->tid )
326                         fprintf(fp," %p", (void*)p->info->owner);
327                 fprintf(fp,"\n");
328         }
329         int64_t lock_total = lock_table.total();
330         int64_t free_total = lock_free.total();
331         printf("lock_items: %jd\n", lock_total);
332         printf("lock_frees: %jd\n", free_total);
333         int64_t missed_locks = lock_table.size - (lock_total + free_total);
334         if( missed_locks )
335                 printf("miss locks: %jd\n", missed_locks);
336 #endif
337 }
338
339 void BC_Trace::dump_buffers(FILE *fp)
340 {
341 #ifdef TRACE_MEMORY
342         memory_table.lock();
343 // Dump buffer table
344         fprintf(fp,"BC_Trace::dump_buffers: buffer table size=%d\n", memory_table.size);
345         for( trace_item *tp=memory_table.first; tp!=0; tp=tp->next ) {
346                 memory_item *p=(memory_item*)tp;
347                 fprintf(fp,"    %d %p %s\n", p->size, p->ptr, p->loc);
348         }
349         memory_table.unlock();
350 #endif
351 }
352
353 void BC_Trace::delete_temps()
354 {
355         file_table.lock();
356         if( file_table.size )
357                 printf("BC_Trace::delete_temps: deleting %d temp files\n", file_table.size);
358         while( file_table.first ) {
359                 file_item *p = (file_item*)file_table.first;
360                 printf("    %s\n", p->value);
361                 ::remove(p->value);
362                 delete p;
363         }
364         file_table.unlock();
365 }
366
367 void BC_Trace::reset_locks()
368 {
369         lock_table.unlock();
370 }
371
372 void BC_Trace::set_temp(char *string)
373 {
374         file_table.lock();
375         file_item *it = new file_item();
376         it->set(string);
377         file_table.append(it);
378         file_table.unlock();
379 }
380
381 void BC_Trace::unset_temp(char *string)
382 {
383         file_table.lock();
384         file_item *p = (file_item *)file_table.last;
385         for( ; p!=0 && strcmp(p->value,string); p=(file_item*)p->previous );
386         if( p ) delete p;
387         file_table.unlock();
388 }
389
390
391 #ifdef TRACE_THREADS
392
393 TheLock TheLocker::the_lock;
394 TheList TheList::the_list;
395
396 int lock_item::table_id = 0;
397
398 void TheList::dbg_add(pthread_t tid, pthread_t owner, const char *nm)
399 {
400         TheLocker the_locker;
401         int i = the_list.size();
402         while( --i >= 0 && !(the_list[i]->tid == tid && the_list[i]->owner == owner) );
403         if( i >= 0 ) {
404                 printf("dbg_add, dup %016lx %s %s\n",
405                         (unsigned long)tid, nm, the_list[i]->name);
406                 return;
407         }
408         the_list.append(new TheDbg(tid, owner, nm));
409 }
410
411 void TheList::dbg_del(pthread_t tid)
412 {
413         TheLocker the_locker;
414         int i = the_list.size();
415         while( --i >= 0 && the_list[i]->tid != tid );
416         if( i < 0 ) {
417                 printf("dbg_del, mis %016lx\n",(unsigned long)tid);
418                 return;
419         }
420         the_list.remove_object_number(i);
421 }
422
423
424 void TheList::dump_threads(FILE *fp)
425 {
426         int i = the_list.size();
427         while( --i >= 0 ) {
428                 fprintf(fp, "thread 0x%012lx, owner 0x%012lx, %s\n",
429                         (unsigned long)the_list[i]->tid, (unsigned long)the_list[i]->owner,
430                         the_list[i]->name);
431         }
432 }
433
434 #else
435 #define dbg_add(t, o, nm) do {} while(0)
436 #define dbg_del(t) do {} while(0)
437 void TheList::dump_threads(FILE *fp)
438 {
439 }
440 #endif
441
442 void BC_Trace::dump_threads(FILE *fp)
443 {
444         TheList::dump_threads(fp);
445 }
446
447
448 void BC_Trace::dump_shm_stat(const char *fn, FILE *fp)
449 {
450         char path[BCTEXTLEN];
451         sprintf(path, "/proc/sys/kernel/%s",fn);
452         FILE *sfp = fopen(path,"r");
453         if( !sfp ) return;
454         uint64_t v = 0;
455         fscanf(sfp, "%ju", &v);
456         fclose(sfp);
457         fprintf(fp, "%s = %ju\n", fn, v);
458 }
459
460 void BC_Trace::dump_shm_stats(FILE *fp)
461 {
462         dump_shm_stat("shmall", fp);
463         dump_shm_stat("shmmax", fp);
464         dump_shm_stat("shmmni", fp);
465         FILE *sfp = fopen("/proc/sysvipc/shm","r");
466         if( !sfp ) return;
467         char line[BCTEXTLEN];
468         int pid = getpid();
469         if( !fgets(line,sizeof(line), sfp) ) return;
470         int64_t used = 0, other = 0;
471         int n_used = 0, n_other = 0;
472         while( fgets(line,sizeof(line), sfp) ) {
473                 int key, shmid, perms, cpid, lpid, uid, gid, cuid, cgid;
474                 int64_t size, nattch, atime, dtime, ctime, rss, swap;
475                 if( sscanf(line,
476                         "%d %d %o %ju %u %u %ju %u %u %u %u %ju %ju %ju %ju %ju",
477                         &key, &shmid, &perms, &size, &cpid, &lpid, &nattch,
478                         &uid, &gid, &cuid, &cgid, &atime, &dtime, &ctime,
479                         &rss, &swap) != 16 ) break;
480                 if( cpid == pid ) {
481                         used += size;
482                         ++n_used;
483                 }
484                 else {
485                         other += size;
486                         ++n_other;
487                 }
488         }
489         fclose(sfp);
490         fprintf(fp, "shmused = %jd (%d items)\n", used, n_used);
491         fprintf(fp, "shmother = %jd (%d items)\n", other, n_other);
492 }
493