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: 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 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 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 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 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 66 ~ClangTest () 67 { 68 } 69 70 virtual bool 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 80 DoTest () 81 { 82 } 83 84 virtual void 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 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 * 220 GetExecutablePath () const 221 { 222 if (m_exe_path.empty()) 223 return NULL; 224 return m_exe_path.c_str(); 225 } 226 227 const char * 228 GetResultFilePath () const 229 { 230 if (m_out_path.empty()) 231 return NULL; 232 return m_out_path.c_str(); 233 } 234 235 void 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 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 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 290 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 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 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