1 /**
2  * Contains implementations of functions called when the
3  *   -profile=gc
4  * switch is thrown.
5  *
6  * Tests for this functionality can be found in test/profile/src/profilegc.d
7  *
8  * Copyright: Copyright Digital Mars 2015 - 2015.
9  * License: Distributed under the
10  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
11  *    (See accompanying file LICENSE)
12  * Authors:   Walter Bright
13  * Source: $(DRUNTIMESRC rt/_tracegc.d)
14  */
15 
16 module rt.tracegc;
17 
18 // version = tracegc;
19 
20 extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims);
21 extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims);
22 extern (C) void _d_callfinalizer(void* p);
23 extern (C) void _d_callinterfacefinalizer(void *p);
24 extern (C) void _d_delclass(Object* p);
25 extern (C) void _d_delinterface(void** p);
26 extern (C) void _d_delmemory(void* *p);
27 extern (C) void* _d_arrayliteralTX(const TypeInfo ti, size_t length);
28 extern (C) void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti,
29     void[] keys, void[] vals);
30 extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n);
31 extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c);
32 extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c);
33 extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p);
34 extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p);
35 extern (C) void* _d_allocmemory(size_t sz);
36 
37 // From GC.BlkInfo_. We cannot import it from core.memory.GC because .stringof
38 // replaces the alias with the private symbol that's not visible from this
39 // module, causing a compile error.
40 private struct BlkInfo
41 {
42     void*  base;
43     size_t size;
44     uint   attr;
45 }
46 extern (C) void* gc_malloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null);
47 extern (C) BlkInfo gc_qalloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null);
48 extern (C) void* gc_calloc(size_t sz, uint ba = 0, const TypeInfo ti = null);
49 extern (C) void* gc_realloc(return scope void* p, size_t sz, uint ba = 0, const TypeInfo ti = null);
50 extern (C) size_t gc_extend(void* p, size_t mx, size_t sz, const TypeInfo ti = null);
51 
52 // Used as wrapper function body to get actual stats.
53 //
54 // Placed here as a separate string constant to simplify maintenance as it is
55 // much more likely to be modified than rest of generation code.
56 enum accumulator = q{
57     import rt.profilegc : accumulate;
58     import core.memory : GC;
59     import core.stdc.string : strstr;
60 
61     static if (is(typeof(ci)))
62         string name = ci.name;
63     else static if (is(typeof(ti)))
64         string name = ti ? ti.toString() : "void[]";
65     else static if (__FUNCTION__ == "rt.tracegc._d_arrayappendcdTrace")
66         string name = "char[]";
67     else static if (__FUNCTION__ == "rt.tracegc._d_arrayappendwdTrace")
68         string name = "wchar[]";
69     else static if (__FUNCTION__ == "rt.tracegc._d_allocmemoryTrace")
70         string name = "closure";
71     else
72         string name = "";
73 
74     version (tracegc)
75     {
76         import core.stdc.stdio;
77 
78         printf("%s file = '%.*s' line = %d function = '%.*s' type = %.*s\n",
79             __FUNCTION__.ptr,
80             file.length, file.ptr,
81             line,
82             funcname.length, funcname.ptr,
83             name.length, name.ptr
84         );
85     }
86 
87     ulong currentlyAllocated = GC.allocatedInCurrentThread;
88 
89     scope(exit)
90     {
91         ulong size = GC.allocatedInCurrentThread - currentlyAllocated;
92         // Skip internal functions.
93         if (size > 0 && strstr(funcname.ptr, "core.internal") is null)
94             accumulate(file, line, funcname, name, size);
95     }
96 };
97 
98 mixin(generateTraceWrappers());
99 //pragma(msg, generateTraceWrappers());
100 
101 ////////////////////////////////////////////////////////////////////////////////
102 // code gen implementation
103 
104 private string generateTraceWrappers()
105 {
106     string code;
107 
108     foreach (name; __traits(allMembers, mixin(__MODULE__)))
109     {
110         static if (name.length > 3 && name[0..3] == "_d_")
111         {
112             mixin("alias Declaration = " ~ name ~ ";");
113             code ~= generateWrapper!Declaration();
114         }
115         static if (name.length > 3 && name[0..3] == "gc_")
116         {
117             mixin("alias Declaration = " ~ name ~ ";");
118             code ~= generateWrapper!(Declaration, ParamPos.back)();
119         }
120     }
121 
122     return code;
123 }
124 
125 static enum ParamPos { front, back }
126 
127 private string generateWrapper(alias Declaration, ParamPos pos = ParamPos.front)()
128 {
129     static size_t findParamIndex(string s)
130     {
131         assert (s[$-1] == ')');
132         size_t brackets = 1;
133         while (brackets != 0)
134         {
135             s = s[0 .. $-1];
136             if (s[$-1] == ')')
137                 ++brackets;
138             if (s[$-1] == '(')
139                 --brackets;
140         }
141 
142         assert(s.length > 1);
143         return s.length - 1;
144     }
145 
146     auto type_string = typeof(Declaration).stringof;
147     auto name = __traits(identifier, Declaration);
148     auto param_idx = findParamIndex(type_string);
149 
150     static if (pos == ParamPos.front)
151         auto new_declaration = type_string[0 .. param_idx] ~ " " ~ name
152             ~ "Trace(string file, int line, string funcname, "
153             ~ type_string[param_idx+1 .. $];
154     else static if (pos == ParamPos.back)
155         auto new_declaration = type_string[0 .. param_idx] ~ " " ~ name
156             ~ "Trace(" ~ type_string[param_idx+1 .. $-1]
157             ~ `, string file = "", int line = 0, string funcname = "")`;
158     else
159         static assert(0);
160     auto call_original = "return "
161         ~ __traits(identifier, Declaration) ~ "(" ~ Arguments!Declaration() ~ ");";
162 
163     return new_declaration ~ "\n{\n" ~
164            accumulator ~ "\n" ~
165            call_original ~ "\n" ~
166            "}\n";
167 }
168 
169 string Arguments(alias Func)()
170 {
171     string result = "";
172 
173     static if (is(typeof(Func) PT == __parameters))
174     {
175         foreach (idx, _; PT)
176             result ~= __traits(identifier, PT[idx .. idx + 1]) ~ ", ";
177     }
178 
179     return result;
180 }
181 
182 unittest
183 {
184     void foo(int x, double y) { }
185     static assert (Arguments!foo == "x, y, ");
186 }