1 module usdt;
2 
3 version(UsdtProbesDisabled)
4 	enum UsdtProbesDisabled = true;
5 else
6 	enum UsdtProbesDisabled = false;
7 
8 @safe @nogc pure nothrow
9 template USDT_PROBE(string provider, string name, Args...) if (UsdtProbesDisabled)
10 {
11 	enum USDT_PROBE = "{}";
12 }
13 
14 @safe @nogc pure nothrow
15 template USDT_PROBE(string provider, string name, Args...) if (!UsdtProbesDisabled)
16 {
17 	static if (Args.length > 12)
18 	{
19 		import std.conv : text;
20 		pragma(msg, "Warning! SystemTap supports up to 12 arguments for probe. ");
21 		pragma(msg, "You use " ~ Args.length.text ~ " arguments");
22 		static assert(0);
23 	}
24 
25 	private enum USDT_PROBE_GDC = `
26 		asm @trusted {
27 			"990: nop
28 				.pushsection .note.stapsdt,\"?\",\"note\"
29 				.balign 4
30 				.4byte 992f-991f, 994f-993f, 3
31 			991: .asciz \"stapsdt\"
32 			992: .balign 4
33 			993: .8byte 990b
34 				.8byte _.stapsdt.base
35 				.8byte 0
36 				.asciz \"%1$s\"
37 				.asciz \"%2$s\"
38 				.asciz \"%3$s\"
39 			994: .balign 4
40 				.popsection
41 
42 				.ifndef _.stapsdt.base
43 					.pushsection .stapsdt.base,\"aG\",\"progbits\", .stapsdt.base, comdat
44 					.weak _.stapsdt.base
45 					.hidden _.stapsdt.base
46 					_.stapsdt.base: .space 1
47 					.size _.stapsdt.base,1
48 					.popsection
49 				.endif"
50 				:: %4$s;
51 		}`;
52 
53 	private enum USDT_PROBE_LDC = "
54 		import ldc.llvmasm;
55 
56 		__asm_trusted (
57 			`990: nop
58 				.pushsection .note.stapsdt,\"?\",\"note\"
59 				.balign 4
60 				.4byte 992f-991f, 994f-993f, 3
61 			991: .asciz \"stapsdt\"
62 			992: .balign 4
63 			993: .8byte 990b
64 				.8byte _.stapsdt.base
65 				.8byte 0
66 				.asciz \"%1$s\"
67 				.asciz \"%2$s\"
68 				.asciz \"%3$s\"
69 			994: .balign 4
70 				.popsection
71 
72 				.ifndef _.stapsdt.base
73 				.pushsection .stapsdt.base,\"aG\",\"progbits\", .stapsdt.base, comdat
74 				.weak _.stapsdt.base
75 				.hidden _.stapsdt.base
76 				_.stapsdt.base: .space 1
77 				.size _.stapsdt.base,1
78 				.popsection
79 				.endif`, \"%4$s\", %5$s
80 	);";
81 
82 	import std.format : format;
83 	version(GNU)
84 		enum USDT_PROBE = USDT_PROBE_GDC.format(
85 			provider, 
86 			name, 
87 			gdcInputOperands!Args,
88 			gdcInputOperandValues!Args,
89 		);
90 	else version(LDC)
91 		enum USDT_PROBE = USDT_PROBE_LDC.format(
92 			provider, 
93 			name, 
94 			ldcInputOperands!Args,
95 			ldcInputOperandConstraints!Args,
96 			ldcInputOperandValues!Args,
97 		);
98 	else version(DigitalMars)
99 		enum USDT_PROBE = "{}";
100 	else
101 		static assert(0, "Unsupported compiler");
102 }
103 
104 static this()
105 {
106 	version(GNU)
107 	{
108 
109 	}
110 	else version(LDC)
111 	{
112 
113 	}
114 	else version(DigitalMars)
115 	{
116 		pragma(msg, "\t[usdt] Attention!");
117 		pragma(msg, "\t[usdt] Digital Mars compiler (dmd) does not support stap probes.");
118 		pragma(msg, "\t[usdt] Stap probes are unavailable.");
119 	}
120 }
121 
122 // Helpers
123 
124 private template ArgSize(Arg)
125 {
126 	import std.traits : isSigned;
127 	static if (isSigned!Arg)
128 		enum ArgSize = cast(int)  Arg.sizeof;
129 	else
130 		enum ArgSize = cast(int) -Arg.sizeof;
131 }
132 
133 unittest
134 {
135 	assert( ArgSize!int == 4 );
136 	assert( ArgSize!uint == -4 );
137 }
138 
139 private template gdcInputOperands(Args...) if (Args.length == 0)
140 {
141 	enum gdcInputOperands = "";
142 }
143 
144 private template gdcInputOperands(Args...) if (Args.length)
145 {
146 	import std.format : format;
147 	static if (Args.length > 1)
148 		enum fmt = "%%n[_SDT_S%1$d]@%%[_SDT_A%1$d] ";
149 	else
150 		enum fmt = "%%n[_SDT_S%1$d]@%%[_SDT_A%1$d]";
151 
152 	enum gdcInputOperands = format(fmt, Args.length) ~ gdcInputOperands!(Args[1..$]);
153 }
154 
155 unittest
156 {
157 	void func(int a, int b)
158 	{
159 		int c;
160 		assert( gdcInputOperands!() == "" );
161 		assert( gdcInputOperands!c == "%n[_SDT_S1]@%[_SDT_A1]" );
162 		assert( gdcInputOperands!(a, b) == "%n[_SDT_S2]@%[_SDT_A2] %n[_SDT_S1]@%[_SDT_A1]" );
163 	}
164 
165 	func(1, 2);
166 }
167 
168 private template gdcInputOperandValues(Args...) if (Args.length == 0)
169 {
170 	enum gdcInputOperandValues = "";
171 }
172 
173 private template gdcInputOperandValues(Args...) if (Args.length)
174 {
175 	static if (Args.length > 1)
176 		enum fmt = `[_SDT_S%1$d] "n" (%3$s), [_SDT_A%1$d] "nor" (%2$s), `;
177 	else
178 		enum fmt = `[_SDT_S%1$d] "n" (%3$s), [_SDT_A%1$d] "nor" (%2$s)`;
179 
180 	import std.format : format;
181 
182 	enum size = ArgSize!(typeof(Args[0]));
183 	enum gdcInputOperandValues = 
184 		format(fmt, Args.length, __traits(identifier, Args[0]), size) ~
185 		gdcInputOperandValues!(Args[1..$]);
186 }
187 
188 unittest
189 {
190 	void func(int a, short b)
191 	{
192 		ubyte c;
193 		assert( gdcInputOperandValues!() == "" );
194 		assert( gdcInputOperandValues!(c) == "[_SDT_S1] \"n\" (-1), [_SDT_A1] \"nor\" (c)" );
195 		assert( gdcInputOperandValues!(a, c) == "[_SDT_S2] \"n\" (4), [_SDT_A2] \"nor\" (a), [_SDT_S1] \"n\" (-1), [_SDT_A1] \"nor\" (c)" );
196 		assert( gdcInputOperandValues!(a, b, c) == "[_SDT_S3] \"n\" (4), [_SDT_A3] \"nor\" (a), [_SDT_S2] \"n\" (2), [_SDT_A2] \"nor\" (b), [_SDT_S1] \"n\" (-1), [_SDT_A1] \"nor\" (c)" );
197 	}
198 
199 	func(1, 2);
200 }
201 
202 private template ldcInputOperands(Args...) if (Args.length == 0)
203 {
204 	enum ldcInputOperands = "";
205 }
206 
207 private template ldcInputOperands(Args...) if (Args.length)
208 {
209 	import std.format : format;
210 	static if (Args.length > 1)
211 		enum fmt = " ${%d:n}@${%d}";
212 	else
213 		enum fmt = "${%d:n}@${%d}";
214 
215 	enum i = (Args.length-1)*2;
216 	enum ldcInputOperands = ldcInputOperands!(Args[1..$]) ~ format(fmt, i, i+1);
217 }
218 
219 unittest
220 {
221 	void func(int a, int b)
222 	{
223 		int c;
224 		assert( ldcInputOperands!() == "" );
225 		assert( ldcInputOperands!(c) == "${0:n}@${1}" );
226 		assert( ldcInputOperands!(a, c) == "${0:n}@${1} ${2:n}@${3}" );
227 	}
228 
229 	func(1, 2);
230 }
231 
232 private template ldcInputOperandConstraints(Args...) if (Args.length == 0)
233 {
234 	enum ldcInputOperandConstraints = "";
235 }
236 
237 private template ldcInputOperandConstraints(Args...) if (Args.length)
238 {
239 	static if (Args.length > 1)
240 		enum prefix = "n, nor, ";
241 	else
242 		enum prefix = "n, nor";
243 
244 	enum ldcInputOperandConstraints = prefix ~ ldcInputOperandConstraints!(Args[1..$]);
245 }
246 
247 unittest
248 {
249 	void func(int a, int b)
250 	{
251 		int c;
252 		assert( ldcInputOperandConstraints!() == "" );
253 		assert( ldcInputOperandConstraints!(c) == "n, nor" );
254 		assert( ldcInputOperandConstraints!(a, c) == "n, nor, n, nor" );
255 	}
256 
257 	func(1, 2);
258 }
259 
260 private template ldcInputOperandValues(Args...) if (Args.length == 0)
261 {
262 	enum ldcInputOperandValues = "";
263 }
264 
265 private template ldcInputOperandValues(Args...) if (Args.length)
266 {
267 	import std.format : format;
268 	static if (Args.length > 1)
269 		enum fmt = `%1$s, (%2$s), `;
270 	else
271 		enum fmt = `%1$s, (%2$s)`;
272 
273 	enum size = ArgSize!(typeof(Args[0]));
274 	enum ldcInputOperandValues = format(fmt, size, __traits(identifier, Args[0])) ~ ldcInputOperandValues!(Args[1..$]);
275 }
276 
277 unittest
278 {
279 	void func(int a, short b)
280 	{
281 		ubyte c;
282 		assert( ldcInputOperandValues!() == "" );
283 		assert( ldcInputOperandValues!(c) == "-1, (c)" );
284 		assert( ldcInputOperandValues!(a, c) == "4, (a), -1, (c)" );
285 		assert( ldcInputOperandValues!(a, b, c) == "4, (a), 2, (b), -1, (c)" );
286 	}
287 
288 	func(1, 2);
289 }
290 
291 @safe @nogc nothrow pure
292 unittest
293 {
294 	mixin USDT_PROBE!("unit", "test");
295 }