1 //===-- lldb_perf_clang.cpp -------------------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "lldb-perf/lib/Timer.h"
11 #include "lldb-perf/lib/Metric.h"
12 #include "lldb-perf/lib/Measurement.h"
13 #include "lldb-perf/lib/Results.h"
14 #include "lldb-perf/lib/TestCase.h"
15 #include "lldb-perf/lib/Xcode.h"
16 #include <iostream>
17 #include <unistd.h>
18 #include <fstream>
19 #include <getopt.h>
20
21 using namespace lldb_perf;
22
23 #define NUM_EXPR_ITERATIONS 3
24 class ClangTest : public TestCase
25 {
26 public:
ClangTest()27 ClangTest () :
28 TestCase(),
29 m_time_create_target ([this] () -> void
30 {
31 m_memory_change_create_target.Start();
32 m_target = m_debugger.CreateTarget(m_exe_path.c_str());
33 m_memory_change_create_target.Stop();
34 }, "time-create-target", "The time it takes to create a target."),
35 m_time_set_bp_main([this] () -> void
__anona65641700202() 36 {
37 m_memory_change_break_main.Start();
38 m_target.BreakpointCreateByName("main");
39 m_memory_change_break_main.Stop();
40 }, "time-set-break-main", "Elapsed time it takes to set a breakpoint at 'main' by name."),
41 m_memory_change_create_target (),
42 m_memory_change_break_main (),
43 m_memory_total (),
44 m_time_launch_stop_main(),
45 m_time_total (),
46 m_expr_first_evaluate([this] (SBFrame frame) -> void
__anona65641700302(SBFrame frame) 47 {
48 frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()").GetError();
49 }, "time-expr", "Elapsed time it takes to evaluate an expression for the first time."),
50 m_expr_frame_zero ([this] (SBFrame frame) -> void
__anona65641700402(SBFrame frame) 51 {
52 frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()").GetError();
53 }, "time-expr-frame-zero", "Elapsed time it takes to evaluate an expression 3 times at frame zero."),
54 m_expr_frame_non_zero ([this] (SBFrame frame) -> void
__anona65641700502(SBFrame frame) 55 {
56 frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()").GetError();
57 }, "time-expr-frame-non-zero", "Elapsed time it takes to evaluate an expression 3 times at a non-zero frame."),
58 m_exe_path(),
59 m_out_path(),
60 m_launch_info (NULL),
61 m_use_dsym (false)
62 {
63 }
64
65 virtual
~ClangTest()66 ~ClangTest ()
67 {
68 }
69
70 virtual bool
Setup(int & argc,const char ** & argv)71 Setup (int& argc, const char**& argv)
72 {
73 if (m_exe_path.empty())
74 return false;
75 m_launch_info.SetArguments(argv, false);
76 return true;
77 }
78
79 void
DoTest()80 DoTest ()
81 {
82 }
83
84 virtual void
TestStep(int counter,ActionWanted & next_action)85 TestStep (int counter, ActionWanted &next_action)
86 {
87 switch (counter)
88 {
89 case 0:
90 {
91 //Xcode::RunCommand(m_debugger,"log enable -f /tmp/packets.txt gdb-remote packets",true);
92
93 m_memory_total.Start();
94 m_time_total.Start();
95
96 // Time creating the target
97 m_time_create_target();
98
99 m_time_set_bp_main();
100
101 m_time_launch_stop_main.Start();
102 const char *clang_argv[] = {
103 "-cc1",
104 "-triple", "x86_64-apple-macosx10.8.0",
105 "-emit-obj",
106 "-mrelax-all",
107 "-disable-free",
108 "-disable-llvm-verifier",
109 "-main-file-name", "main.cpp",
110 "-mrelocation-model", "pic",
111 "-pic-level", "2",
112 "-mdisable-fp-elim",
113 "-masm-verbose",
114 "-munwind-tables",
115 "-target-cpu", "core2",
116 "-target-linker-version", "132.10.1",
117 "-v",
118 "-g",
119 "-resource-dir", "/tmp/clang-176809/llvm-build/build/Debug/bin/../lib/clang/3.3",
120 "-O0",
121 "-fdeprecated-macro",
122 "-fdebug-compilation-dir", "/tmp/clang-176809/llvm-build/build/Debug/bin",
123 "-ferror-limit", "19",
124 "-fmessage-length", "298",
125 "-stack-protector", "1",
126 "-mstackrealign",
127 "-fblocks",
128 "-fobjc-runtime=macosx-10.8.0",
129 "-fobjc-dispatch-method=mixed",
130 "-fobjc-default-synthesize-properties",
131 "-fencode-extended-block-signature",
132 "-fcxx-exceptions",
133 "-fexceptions",
134 "-fdiagnostics-show-option",
135 "-fcolor-diagnostics",
136 "-backend-option",
137 "-vectorize-loops",
138 "-o", "/tmp/main.o",
139 "-x", "c++",
140 "/tmp/main.cpp",
141 NULL };
142 SBLaunchInfo launch_info(clang_argv);
143 Launch (launch_info);
144 }
145 break;
146 case 1:
147 puts("stop");
148 m_time_launch_stop_main.Stop();
149 m_time_total.Stop();
150 case 2:
151 {
152 SBFrame frame (m_thread.GetFrameAtIndex(0));
153
154 // Time the first expression evaluation
155 m_expr_first_evaluate(frame);
156
157 SBValue result;
158 for (size_t i=0; i<NUM_EXPR_ITERATIONS; ++i)
159 {
160 m_expr_frame_zero(frame);
161 }
162 m_target.BreakpointCreateByName("DeclContext::lookup");
163 next_action.Continue();
164 }
165 break;
166 case 3:
167 {
168 SBFrame frame (m_thread.GetFrameAtIndex(21));
169 SBValue result;
170 for (size_t i=0; i<NUM_EXPR_ITERATIONS; ++i)
171 {
172 m_expr_frame_non_zero(frame);
173 }
174 m_target.BreakpointCreateByName("DeclContext::lookup");
175 next_action.Continue();
176 }
177 break;
178 default:
179 m_memory_total.Stop();
180 next_action.Kill();
181 break;
182 }
183 }
184
185 void
WriteResults(Results & results)186 WriteResults (Results &results)
187 {
188 Results::Dictionary& results_dict = results.GetDictionary();
189
190 m_time_set_bp_main.WriteAverageAndStandardDeviation(results);
191 results_dict.Add ("memory-change-create-target",
192 "Memory increase that occurs due to creating the target.",
193 m_memory_change_create_target.GetDeltaValue().GetResult(NULL, NULL));
194
195 results_dict.Add ("memory-change-break-main",
196 "Memory increase that occurs due to setting a breakpoint at main by name.",
197 m_memory_change_break_main.GetDeltaValue().GetResult(NULL, NULL));
198
199 m_time_create_target.WriteAverageAndStandardDeviation(results);
200 m_expr_first_evaluate.WriteAverageAndStandardDeviation(results);
201 m_expr_frame_zero.WriteAverageAndStandardDeviation(results);
202 m_expr_frame_non_zero.WriteAverageAndStandardDeviation(results);
203 results_dict.Add ("memory-total-break-main",
204 "The total memory that the current process is using after setting the first breakpoint.",
205 m_memory_total.GetStopValue().GetResult(NULL, NULL));
206
207 results_dict.AddDouble("time-launch-stop-main",
208 "The time it takes to launch the process and stop at main.",
209 m_time_launch_stop_main.GetDeltaValue());
210
211 results_dict.AddDouble("time-total",
212 "The time it takes to create the target, set breakpoint at main, launch clang and hit the breakpoint at main.",
213 m_time_total.GetDeltaValue());
214 results.Write(GetResultFilePath());
215 }
216
217
218
219 const char *
GetExecutablePath() const220 GetExecutablePath () const
221 {
222 if (m_exe_path.empty())
223 return NULL;
224 return m_exe_path.c_str();
225 }
226
227 const char *
GetResultFilePath() const228 GetResultFilePath () const
229 {
230 if (m_out_path.empty())
231 return NULL;
232 return m_out_path.c_str();
233 }
234
235 void
SetExecutablePath(const char * path)236 SetExecutablePath (const char *path)
237 {
238 if (path && path[0])
239 m_exe_path = path;
240 else
241 m_exe_path.clear();
242 }
243
244 void
SetResultFilePath(const char * path)245 SetResultFilePath (const char *path)
246 {
247 if (path && path[0])
248 m_out_path = path;
249 else
250 m_out_path.clear();
251 }
252
253 void
SetUseDSYM(bool b)254 SetUseDSYM (bool b)
255 {
256 m_use_dsym = b;
257 }
258
259
260
261 private:
262 // C++ formatters
263 TimeMeasurement<std::function<void()>> m_time_create_target;
264 TimeMeasurement<std::function<void()>> m_time_set_bp_main;
265 MemoryGauge m_memory_change_create_target;
266 MemoryGauge m_memory_change_break_main;
267 MemoryGauge m_memory_total;
268 TimeGauge m_time_launch_stop_main;
269 TimeGauge m_time_total;
270 TimeMeasurement<std::function<void(SBFrame)>> m_expr_first_evaluate;
271 TimeMeasurement<std::function<void(SBFrame)>> m_expr_frame_zero;
272 TimeMeasurement<std::function<void(SBFrame)>> m_expr_frame_non_zero;
273 std::string m_exe_path;
274 std::string m_out_path;
275 SBLaunchInfo m_launch_info;
276 bool m_use_dsym;
277
278 };
279
280
281 struct Options
282 {
283 std::string clang_path;
284 std::string out_file;
285 bool verbose;
286 bool use_dsym;
287 bool error;
288 bool print_help;
289
OptionsOptions290 Options() :
291 verbose (false),
292 error (false),
293 print_help (false)
294 {
295 }
296 };
297
298 static struct option g_long_options[] = {
299 { "verbose", no_argument, NULL, 'v' },
300 { "clang", required_argument, NULL, 'c' },
301 { "out-file", required_argument, NULL, 'o' },
302 { "dsym", no_argument, NULL, 'd' },
303 { NULL, 0, NULL, 0 }
304 };
305
306
307 std::string
GetShortOptionString(struct option * long_options)308 GetShortOptionString (struct option *long_options)
309 {
310 std::string option_string;
311 for (int i = 0; long_options[i].name != NULL; ++i)
312 {
313 if (long_options[i].flag == NULL)
314 {
315 option_string.push_back ((char) long_options[i].val);
316 switch (long_options[i].has_arg)
317 {
318 default:
319 case no_argument:
320 break;
321 case required_argument:
322 option_string.push_back (':');
323 break;
324 case optional_argument:
325 option_string.append (2, ':');
326 break;
327 }
328 }
329 }
330 return option_string;
331 }
332
main(int argc,const char * argv[])333 int main(int argc, const char * argv[])
334 {
335
336 // Prepare for & make calls to getopt_long_only.
337
338 std::string short_option_string (GetShortOptionString(g_long_options));
339
340 ClangTest test;
341
342 Options option_data;
343 bool done = false;
344
345 #if __GLIBC__
346 optind = 0;
347 #else
348 optreset = 1;
349 optind = 1;
350 #endif
351 while (!done)
352 {
353 int long_options_index = -1;
354 const int short_option = ::getopt_long_only (argc,
355 const_cast<char **>(argv),
356 short_option_string.c_str(),
357 g_long_options,
358 &long_options_index);
359
360 switch (short_option)
361 {
362 case 0:
363 // Already handled
364 break;
365
366 case -1:
367 done = true;
368 break;
369
370 case '?':
371 option_data.print_help = true;
372 break;
373
374 case 'h':
375 option_data.print_help = true;
376 break;
377
378 case 'v':
379 option_data.verbose = true;
380 break;
381
382 case 'c':
383 {
384 SBFileSpec file(optarg);
385 if (file.Exists())
386 test.SetExecutablePath(optarg);
387 else
388 fprintf(stderr, "error: file specified in --clang (-c) option doesn't exist: '%s'\n", optarg);
389 }
390 break;
391
392 case 'o':
393 test.SetResultFilePath(optarg);
394 break;
395
396 case 'd':
397 test.SetUseDSYM(true);
398 break;
399
400 default:
401 option_data.error = true;
402 option_data.print_help = true;
403 fprintf (stderr, "error: unrecognized option %c\n", short_option);
404 break;
405 }
406 }
407
408
409 if (test.GetExecutablePath() == NULL)
410 {
411 // --clang is mandatory
412 option_data.print_help = true;
413 option_data.error = true;
414 fprintf (stderr, "error: the '--clang=PATH' option is mandatory\n");
415 }
416
417 if (option_data.print_help)
418 {
419 puts(R"(
420 NAME
421 lldb_perf_clang -- a tool that measures LLDB peformance while debugging clang.
422
423 SYNOPSIS
424 lldb_perf_clang --clang=PATH [--out-file=PATH --verbose --dsym] -- [clang options]
425
426 DESCRIPTION
427 Runs a set of static timing and memory tasks against clang and outputs results
428 to a plist file.
429 )");
430 }
431 if (option_data.error)
432 {
433 exit(1);
434 }
435
436 // Update argc and argv after parsing options
437 argc -= optind;
438 argv += optind;
439
440 test.SetVerbose(true);
441 TestCase::Run(test, argc, argv);
442 return 0;
443 }
444
445