comparison druntime/src/compiler/ldc/trace.d @ 1458:e0b2d67cfe7c

Added druntime (this should be removed once it works).
author Robert Clipsham <robert@octarineparrot.com>
date Tue, 02 Jun 2009 17:43:06 +0100
parents
children
comparison
equal deleted inserted replaced
1456:7b218ec1044f 1458:e0b2d67cfe7c
1 /**
2 * Contains support code for code profiling.
3 *
4 * Copyright: Copyright Digital Mars 1995 - 2009.
5 * License: <a href="http://www.boost.org/LICENSE_1_0.txt>Boost License 1.0</a>.
6 * Authors: Walter Bright, Sean Kelly
7 *
8 * Copyright Digital Mars 1995 - 2009.
9 * Distributed under the Boost Software License, Version 1.0.
10 * (See accompanying file LICENSE_1_0.txt or copy at
11 * http://www.boost.org/LICENSE_1_0.txt)
12 */
13 module rt.trace;
14
15 private
16 {
17 import rt.util.string;
18 import core.stdc.ctype;
19 import core.stdc.stdio;
20 import core.stdc.string;
21 import core.stdc.stdlib;
22 }
23
24 extern (C):
25
26 char* unmangle_ident(char*); // from DMC++ runtime library
27
28 alias long timer_t;
29
30 /////////////////////////////////////
31 //
32
33 struct SymPair
34 {
35 SymPair* next;
36 Symbol* sym; // function that is called
37 uint count; // number of times sym is called
38 }
39
40 /////////////////////////////////////
41 // A Symbol for each function name.
42
43 struct Symbol
44 {
45 Symbol* Sl, Sr; // left, right children
46 SymPair* Sfanin; // list of calling functions
47 SymPair* Sfanout; // list of called functions
48 timer_t totaltime; // aggregate time
49 timer_t functime; // time excluding subfunction calls
50 ubyte Sflags;
51 char[] Sident; // name of symbol
52 }
53
54 const ubyte SFvisited = 1; // visited
55
56
57 //////////////////////////////////
58 // Build a linked list of these.
59
60 struct Stack
61 {
62 Stack* prev;
63 Symbol* sym;
64 timer_t starttime; // time when function was entered
65 timer_t ohd; // overhead of all the bookkeeping code
66 timer_t subtime; // time used by all subfunctions
67 }
68
69 __gshared // doesn't work with multithreaded code anyway
70 {
71 Symbol* root; // root of symbol table
72
73 Stack* stack_freelist;
74 Stack* trace_tos; // top of stack
75 int trace_inited; // !=0 if initialized
76 timer_t trace_ohd;
77
78 Symbol** psymbols;
79 uint nsymbols; // number of symbols
80
81 string trace_logfilename = "trace.log";
82 FILE* fplog;
83
84 string trace_deffilename = "trace.def";
85 FILE* fpdef;
86 }
87
88 ////////////////////////////////////////
89 // Set file name for output.
90 // A file name of "" means write results to stdout.
91 // Returns:
92 // 0 success
93 // !=0 failure
94
95 int trace_setlogfilename(string name)
96 {
97 trace_logfilename = name;
98 return 0;
99 }
100
101 ////////////////////////////////////////
102 // Set file name for output.
103 // A file name of "" means write results to stdout.
104 // Returns:
105 // 0 success
106 // !=0 failure
107
108 int trace_setdeffilename(string name)
109 {
110 trace_deffilename = name;
111 return 0;
112 }
113
114 ////////////////////////////////////////
115 // Output optimal function link order.
116
117 static void trace_order(Symbol *s)
118 {
119 while (s)
120 {
121 trace_place(s,0);
122 if (s.Sl)
123 trace_order(s.Sl);
124 s = s.Sr;
125 }
126 }
127
128 //////////////////////////////////////////////
129 //
130
131 static Stack* stack_malloc()
132 { Stack *s;
133
134 if (stack_freelist)
135 { s = stack_freelist;
136 stack_freelist = s.prev;
137 }
138 else
139 s = cast(Stack *)trace_malloc(Stack.sizeof);
140 return s;
141 }
142
143 //////////////////////////////////////////////
144 //
145
146 static void stack_free(Stack *s)
147 {
148 s.prev = stack_freelist;
149 stack_freelist = s;
150 }
151
152 //////////////////////////////////////
153 // Qsort() comparison routine for array of pointers to SymPair's.
154
155 static int sympair_cmp(in void* e1, in void* e2)
156 { SymPair** psp1;
157 SymPair** psp2;
158
159 psp1 = cast(SymPair**)e1;
160 psp2 = cast(SymPair**)e2;
161
162 return (*psp2).count - (*psp1).count;
163 }
164
165 //////////////////////////////////////
166 // Place symbol s, and then place any fan ins or fan outs with
167 // counts greater than count.
168
169 static void trace_place(Symbol *s, uint count)
170 { SymPair* sp;
171 SymPair** base;
172
173 if (!(s.Sflags & SFvisited))
174 { size_t num;
175 uint u;
176
177 //printf("\t%.*s\t%u\n", s.Sident, count);
178 fprintf(fpdef,"\t%.*s\n", s.Sident);
179 s.Sflags |= SFvisited;
180
181 // Compute number of items in array
182 num = 0;
183 for (sp = s.Sfanin; sp; sp = sp.next)
184 num++;
185 for (sp = s.Sfanout; sp; sp = sp.next)
186 num++;
187 if (!num)
188 return;
189
190 // Allocate and fill array
191 base = cast(SymPair**)trace_malloc(SymPair.sizeof * num);
192 u = 0;
193 for (sp = s.Sfanin; sp; sp = sp.next)
194 base[u++] = sp;
195 for (sp = s.Sfanout; sp; sp = sp.next)
196 base[u++] = sp;
197
198 // Sort array
199 qsort(base, num, (SymPair *).sizeof, &sympair_cmp);
200
201 //for (u = 0; u < num; u++)
202 //printf("\t\t%.*s\t%u\n", base[u].sym.Sident, base[u].count);
203
204 // Place symbols
205 for (u = 0; u < num; u++)
206 {
207 if (base[u].count >= count)
208 { uint u2;
209 uint c2;
210
211 u2 = (u + 1 < num) ? u + 1 : u;
212 c2 = base[u2].count;
213 if (c2 < count)
214 c2 = count;
215 trace_place(base[u].sym,c2);
216 }
217 else
218 break;
219 }
220
221 // Clean up
222 trace_free(base);
223 }
224 }
225
226 /////////////////////////////////////
227 // Initialize and terminate.
228
229 static this()
230 {
231 trace_init();
232 }
233
234 static ~this()
235 {
236 trace_term();
237 }
238
239 ///////////////////////////////////
240 // Report results.
241 // Also compute nsymbols.
242
243 static void trace_report(Symbol* s)
244 { SymPair* sp;
245 uint count;
246
247 //printf("trace_report()\n");
248 while (s)
249 { nsymbols++;
250 if (s.Sl)
251 trace_report(s.Sl);
252 fprintf(fplog,"------------------\n");
253 count = 0;
254 for (sp = s.Sfanin; sp; sp = sp.next)
255 {
256 fprintf(fplog,"\t%5d\t%.*s\n", sp.count, sp.sym.Sident);
257 count += sp.count;
258 }
259 fprintf(fplog,"%.*s\t%u\t%lld\t%lld\n",s.Sident,count,s.totaltime,s.functime);
260 for (sp = s.Sfanout; sp; sp = sp.next)
261 {
262 fprintf(fplog,"\t%5d\t%.*s\n",sp.count,sp.sym.Sident);
263 }
264 s = s.Sr;
265 }
266 }
267
268 ////////////////////////////////////
269 // Allocate and fill array of symbols.
270
271 static void trace_array(Symbol *s)
272 {
273 __gshared uint u;
274
275 if (!psymbols)
276 { u = 0;
277 psymbols = cast(Symbol **)trace_malloc((Symbol *).sizeof * nsymbols);
278 }
279 while (s)
280 {
281 psymbols[u++] = s;
282 trace_array(s.Sl);
283 s = s.Sr;
284 }
285 }
286
287
288 //////////////////////////////////////
289 // Qsort() comparison routine for array of pointers to Symbol's.
290
291 static int symbol_cmp(in void* e1, in void* e2)
292 { Symbol** ps1;
293 Symbol** ps2;
294 timer_t diff;
295
296 ps1 = cast(Symbol **)e1;
297 ps2 = cast(Symbol **)e2;
298
299 diff = (*ps2).functime - (*ps1).functime;
300 return (diff == 0) ? 0 : ((diff > 0) ? 1 : -1);
301 }
302
303
304 ///////////////////////////////////
305 // Report function timings
306
307 static void trace_times(Symbol* root)
308 { uint u;
309 timer_t freq;
310
311 // Sort array
312 qsort(psymbols, nsymbols, (Symbol *).sizeof, &symbol_cmp);
313
314 // Print array
315 QueryPerformanceFrequency(&freq);
316 fprintf(fplog,"\n======== Timer Is %lld Ticks/Sec, Times are in Microsecs ========\n\n",freq);
317 fprintf(fplog," Num Tree Func Per\n");
318 fprintf(fplog," Calls Time Time Call\n\n");
319 for (u = 0; u < nsymbols; u++)
320 { Symbol* s = psymbols[u];
321 timer_t tl,tr;
322 timer_t fl,fr;
323 timer_t pl,pr;
324 timer_t percall;
325 SymPair* sp;
326 uint calls;
327 char[] id;
328
329 version (Windows)
330 {
331 char* p = (s.Sident ~ '\0').ptr;
332 p = unmangle_ident(p);
333 if (p)
334 id = p[0 .. strlen(p)];
335 }
336 if (!id)
337 id = s.Sident;
338 calls = 0;
339 for (sp = s.Sfanin; sp; sp = sp.next)
340 calls += sp.count;
341 if (calls == 0)
342 calls = 1;
343
344 version (all)
345 {
346 tl = (s.totaltime * 1000000) / freq;
347 fl = (s.functime * 1000000) / freq;
348 percall = s.functime / calls;
349 pl = (s.functime * 1000000) / calls / freq;
350
351 fprintf(fplog,"%7d%12lld%12lld%12lld %.*s\n",
352 calls,tl,fl,pl,id);
353 }
354 else
355 {
356 tl = s.totaltime / freq;
357 tr = ((s.totaltime - tl * freq) * 10000000) / freq;
358
359 fl = s.functime / freq;
360 fr = ((s.functime - fl * freq) * 10000000) / freq;
361
362 percall = s.functime / calls;
363 pl = percall / freq;
364 pr = ((percall - pl * freq) * 10000000) / freq;
365
366 fprintf(fplog,"%7d\t%3lld.%07lld\t%3lld.%07lld\t%3lld.%07lld\t%.*s\n",
367 calls,tl,tr,fl,fr,pl,pr,id);
368 }
369 if (id !is s.Sident)
370 free(id.ptr);
371 }
372 }
373
374
375 ///////////////////////////////////
376 // Initialize.
377
378 static void trace_init()
379 {
380 if (!trace_inited)
381 {
382 trace_inited = 1;
383
384 { // See if we can determine the overhead.
385 uint u;
386 timer_t starttime;
387 timer_t endtime;
388 Stack *st;
389
390 st = trace_tos;
391 trace_tos = null;
392 QueryPerformanceCounter(&starttime);
393 for (u = 0; u < 100; u++)
394 {
395 asm
396 {
397 call _trace_pro_n ;
398 db 0 ;
399 call _trace_epi_n ;
400 }
401 }
402 QueryPerformanceCounter(&endtime);
403 trace_ohd = (endtime - starttime) / u;
404 //printf("trace_ohd = %lld\n",trace_ohd);
405 if (trace_ohd > 0)
406 trace_ohd--; // round down
407 trace_tos = st;
408 }
409 }
410 }
411
412 /////////////////////////////////
413 // Terminate.
414
415 void trace_term()
416 {
417 //printf("trace_term()\n");
418 if (trace_inited == 1)
419 { Stack *n;
420
421 trace_inited = 2;
422
423 // Free remainder of the stack
424 while (trace_tos)
425 {
426 n = trace_tos.prev;
427 stack_free(trace_tos);
428 trace_tos = n;
429 }
430
431 while (stack_freelist)
432 {
433 n = stack_freelist.prev;
434 stack_free(stack_freelist);
435 stack_freelist = n;
436 }
437
438 // Merge in data from any existing file
439 trace_merge();
440
441 // Report results
442 fplog = fopen(trace_logfilename.ptr, "w");
443 if (fplog)
444 { nsymbols = 0;
445 trace_report(root);
446 trace_array(root);
447 trace_times(root);
448 fclose(fplog);
449 }
450
451 // Output function link order
452 fpdef = fopen(trace_deffilename.ptr,"w");
453 if (fpdef)
454 { fprintf(fpdef,"\nFUNCTIONS\n");
455 trace_order(root);
456 fclose(fpdef);
457 }
458
459 trace_free(psymbols);
460 psymbols = null;
461 }
462 }
463
464 /////////////////////////////////
465 // Our storage allocator.
466
467 static void *trace_malloc(size_t nbytes)
468 { void *p;
469
470 p = malloc(nbytes);
471 if (!p)
472 exit(EXIT_FAILURE);
473 return p;
474 }
475
476 static void trace_free(void *p)
477 {
478 free(p);
479 }
480
481 //////////////////////////////////////////////
482 //
483
484 static Symbol* trace_addsym(char[] id)
485 {
486 Symbol** parent;
487 Symbol* rover;
488 Symbol* s;
489 int cmp;
490 char c;
491
492 //printf("trace_addsym('%s',%d)\n",p,len);
493 parent = &root;
494 rover = *parent;
495 while (rover !is null) // while we haven't run out of tree
496 {
497 cmp = dstrcmp(id, rover.Sident);
498 if (cmp == 0)
499 {
500 return rover;
501 }
502 parent = (cmp < 0) ? /* if we go down left side */
503 &(rover.Sl) : /* then get left child */
504 &(rover.Sr); /* else get right child */
505 rover = *parent; /* get child */
506 }
507 /* not in table, so insert into table */
508 s = cast(Symbol *)trace_malloc(Symbol.sizeof);
509 memset(s,0,Symbol.sizeof);
510 s.Sident = id;
511 *parent = s; // link new symbol into tree
512 return s;
513 }
514
515 /***********************************
516 * Add symbol s with count to SymPair list.
517 */
518
519 static void trace_sympair_add(SymPair** psp, Symbol* s, uint count)
520 { SymPair* sp;
521
522 for (; 1; psp = &sp.next)
523 {
524 sp = *psp;
525 if (!sp)
526 {
527 sp = cast(SymPair *)trace_malloc(SymPair.sizeof);
528 sp.sym = s;
529 sp.count = 0;
530 sp.next = null;
531 *psp = sp;
532 break;
533 }
534 else if (sp.sym == s)
535 {
536 break;
537 }
538 }
539 sp.count += count;
540 }
541
542 //////////////////////////////////////////////
543 //
544
545 static void trace_pro(char[] id)
546 {
547 Stack* n;
548 Symbol* s;
549 timer_t starttime;
550 timer_t t;
551
552 QueryPerformanceCounter(&starttime);
553 if (id.length == 0)
554 return;
555 if (!trace_inited)
556 trace_init(); // initialize package
557 n = stack_malloc();
558 n.prev = trace_tos;
559 trace_tos = n;
560 s = trace_addsym(id);
561 trace_tos.sym = s;
562 if (trace_tos.prev)
563 {
564 Symbol* prev;
565 int i;
566
567 // Accumulate Sfanout and Sfanin
568 prev = trace_tos.prev.sym;
569 trace_sympair_add(&prev.Sfanout,s,1);
570 trace_sympair_add(&s.Sfanin,prev,1);
571 }
572 QueryPerformanceCounter(&t);
573 trace_tos.starttime = starttime;
574 trace_tos.ohd = trace_ohd + t - starttime;
575 trace_tos.subtime = 0;
576 //printf("trace_tos.ohd=%lld, trace_ohd=%lld + t=%lld - starttime=%lld\n",
577 // trace_tos.ohd,trace_ohd,t,starttime);
578 }
579
580 /////////////////////////////////////////
581 //
582
583 static void trace_epi()
584 { Stack* n;
585 timer_t endtime;
586 timer_t t;
587 timer_t ohd;
588
589 //printf("trace_epi()\n");
590 if (trace_tos)
591 {
592 timer_t starttime;
593 timer_t totaltime;
594
595 QueryPerformanceCounter(&endtime);
596 starttime = trace_tos.starttime;
597 totaltime = endtime - starttime - trace_tos.ohd;
598 if (totaltime < 0)
599 { //printf("endtime=%lld - starttime=%lld - trace_tos.ohd=%lld < 0\n",
600 // endtime,starttime,trace_tos.ohd);
601 totaltime = 0; // round off error, just make it 0
602 }
603
604 // totaltime is time spent in this function + all time spent in
605 // subfunctions - bookkeeping overhead.
606 trace_tos.sym.totaltime += totaltime;
607
608 //if (totaltime < trace_tos.subtime)
609 //printf("totaltime=%lld < trace_tos.subtime=%lld\n",totaltime,trace_tos.subtime);
610 trace_tos.sym.functime += totaltime - trace_tos.subtime;
611 ohd = trace_tos.ohd;
612 n = trace_tos.prev;
613 stack_free(trace_tos);
614 trace_tos = n;
615 if (n)
616 { QueryPerformanceCounter(&t);
617 n.ohd += ohd + t - endtime;
618 n.subtime += totaltime;
619 //printf("n.ohd = %lld\n",n.ohd);
620 }
621 }
622 }
623
624
625 ////////////////////////// FILE INTERFACE /////////////////////////
626
627 /////////////////////////////////////
628 // Read line from file fp.
629 // Returns:
630 // trace_malloc'd line buffer
631 // null if end of file
632
633 static char* trace_readline(FILE* fp)
634 { int c;
635 int dim;
636 int i;
637 char *buf;
638
639 //printf("trace_readline(%p)\n", fp);
640 i = 0;
641 dim = 0;
642 buf = null;
643 while (1)
644 {
645 if (i == dim)
646 { char *p;
647
648 dim += 80;
649 p = cast(char *)trace_malloc(dim);
650 memcpy(p,buf,i);
651 trace_free(buf);
652 buf = p;
653 }
654 c = fgetc(fp);
655 switch (c)
656 {
657 case EOF:
658 if (i == 0)
659 { trace_free(buf);
660 return null;
661 }
662 case '\n':
663 goto L1;
664 default:
665 break;
666 }
667 buf[i] = cast(char)c;
668 i++;
669 }
670 L1:
671 buf[i] = 0;
672 //printf("line '%s'\n",buf);
673 return buf;
674 }
675
676 //////////////////////////////////////
677 // Skip space
678
679 static char *skipspace(char *p)
680 {
681 while (isspace(*p))
682 p++;
683 return p;
684 }
685
686 ////////////////////////////////////////////////////////
687 // Merge in profiling data from existing file.
688
689 static void trace_merge()
690 { FILE *fp;
691 char *buf;
692 char *p;
693 uint count;
694 Symbol *s;
695 SymPair *sfanin;
696 SymPair **psp;
697
698 if (trace_logfilename && (fp = fopen(trace_logfilename.ptr,"r")) !is null)
699 {
700 buf = null;
701 sfanin = null;
702 psp = &sfanin;
703 while (1)
704 {
705 trace_free(buf);
706 buf = trace_readline(fp);
707 if (!buf)
708 break;
709 switch (*buf)
710 {
711 case '=': // ignore rest of file
712 trace_free(buf);
713 goto L1;
714 case ' ':
715 case '\t': // fan in or fan out line
716 count = strtoul(buf,&p,10);
717 if (p == buf) // if invalid conversion
718 continue;
719 p = skipspace(p);
720 if (!*p)
721 continue;
722 s = trace_addsym(p[0 .. strlen(p)]);
723 trace_sympair_add(psp,s,count);
724 break;
725 default:
726 if (!isalpha(*buf))
727 {
728 if (!sfanin)
729 psp = &sfanin;
730 continue; // regard unrecognized line as separator
731 }
732 case '?':
733 case '_':
734 case '$':
735 case '@':
736 p = buf;
737 while (isgraph(*p))
738 p++;
739 *p = 0;
740 //printf("trace_addsym('%s')\n",buf);
741 s = trace_addsym(buf[0 .. strlen(buf)]);
742 if (s.Sfanin)
743 { SymPair *sp;
744
745 for (; sfanin; sfanin = sp)
746 {
747 trace_sympair_add(&s.Sfanin,sfanin.sym,sfanin.count);
748 sp = sfanin.next;
749 trace_free(sfanin);
750 }
751 }
752 else
753 { s.Sfanin = sfanin;
754 }
755 sfanin = null;
756 psp = &s.Sfanout;
757
758 { timer_t t;
759
760 p++;
761 count = strtoul(p,&p,10);
762 t = cast(long)strtoull(p,&p,10);
763 s.totaltime += t;
764 t = cast(long)strtoull(p,&p,10);
765 s.functime += t;
766 }
767 break;
768 }
769 }
770 L1:
771 fclose(fp);
772 }
773 }
774
775 ////////////////////////// COMPILER INTERFACE /////////////////////
776
777 /////////////////////////////////////////////
778 // Function called by trace code in function prolog.
779
780 void _trace_pro_n()
781 {
782 /* Length of string is either:
783 * db length
784 * ascii string
785 * or:
786 * db 0x0FF
787 * db 0
788 * dw length
789 * ascii string
790 */
791
792 version (OSX)
793 { // 16 byte align stack
794 asm
795 { naked ;
796 pushad ;
797 mov ECX,8*4[ESP] ;
798 xor EAX,EAX ;
799 mov AL,[ECX] ;
800 cmp AL,0xFF ;
801 jne L1 ;
802 cmp byte ptr 1[ECX],0 ;
803 jne L1 ;
804 mov AX,2[ECX] ;
805 add 8*4[ESP],3 ;
806 add ECX,3 ;
807 L1: inc EAX ;
808 inc ECX ;
809 add 8*4[ESP],EAX ;
810 dec EAX ;
811 sub ESP,4 ;
812 push ECX ;
813 push EAX ;
814 call trace_pro ;
815 add ESP,12 ;
816 popad ;
817 ret ;
818 }
819 }
820 else
821 {
822 asm
823 { naked ;
824 pushad ;
825 mov ECX,8*4[ESP] ;
826 xor EAX,EAX ;
827 mov AL,[ECX] ;
828 cmp AL,0xFF ;
829 jne L1 ;
830 cmp byte ptr 1[ECX],0 ;
831 jne L1 ;
832 mov AX,2[ECX] ;
833 add 8*4[ESP],3 ;
834 add ECX,3 ;
835 L1: inc EAX ;
836 inc ECX ;
837 add 8*4[ESP],EAX ;
838 dec EAX ;
839 push ECX ;
840 push EAX ;
841 call trace_pro ;
842 add ESP,8 ;
843 popad ;
844 ret ;
845 }
846 }
847 }
848
849 /////////////////////////////////////////////
850 // Function called by trace code in function epilog.
851
852
853 void _trace_epi_n()
854 {
855 version (OSX)
856 { // 16 byte align stack
857 asm
858 { naked ;
859 pushad ;
860 sub ESP,12 ;
861 }
862 trace_epi();
863 asm
864 {
865 add ESP,12 ;
866 popad ;
867 ret ;
868 }
869 }
870 else
871 {
872 asm
873 { naked ;
874 pushad ;
875 }
876 trace_epi();
877 asm
878 {
879 popad ;
880 ret ;
881 }
882 }
883 }
884
885
886 version (Windows)
887 {
888 extern (Windows)
889 {
890 export int QueryPerformanceCounter(timer_t *);
891 export int QueryPerformanceFrequency(timer_t *);
892 }
893 }
894 else version (X86)
895 {
896 extern (D)
897 {
898 void QueryPerformanceCounter(timer_t* ctr)
899 {
900 asm
901 { naked ;
902 mov ECX,EAX ;
903 rdtsc ;
904 mov [ECX],EAX ;
905 mov 4[ECX],EDX ;
906 ret ;
907 }
908 }
909
910 void QueryPerformanceFrequency(timer_t* freq)
911 {
912 *freq = 3579545;
913 }
914 }
915 }
916 else
917 {
918 static assert(0);
919 }