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