#include #include "lldb-perf/lib/Timer.h" #include "lldb-perf/lib/Metric.h" #include "lldb-perf/lib/Measurement.h" #include "lldb-perf/lib/TestCase.h" #include "lldb-perf/lib/Xcode.h" #include #include #include using namespace lldb_perf; class StepTest : public TestCase { typedef void (*no_function) (void); public: StepTest(bool use_single_stepping = false) : m_main_source("stepping-testcase.cpp"), m_use_single_stepping(use_single_stepping), m_time_measurements(nullptr) { } virtual ~StepTest() {} virtual bool Setup (int& argc, const char**& argv) { TestCase::Setup (argc, argv); // Toggle the fast stepping command on or off as required. const char *single_step_cmd = "settings set target.use-fast-stepping false"; const char *fast_step_cmd = "settings set target.use-fast-stepping true"; const char *cmd_to_use; if (m_use_single_stepping) cmd_to_use = single_step_cmd; else cmd_to_use = fast_step_cmd; SBCommandReturnObject return_object; m_debugger.GetCommandInterpreter().HandleCommand(cmd_to_use, return_object); if (!return_object.Succeeded()) { if (return_object.GetError() != NULL) printf ("Got an error running settings set: %s.\n", return_object.GetError()); else printf ("Failed running settings set, no error.\n"); } m_target = m_debugger.CreateTarget(m_app_path.c_str()); m_first_bp = m_target.BreakpointCreateBySourceRegex("Here is some code to stop at originally.", m_main_source); const char* file_arg = m_app_path.c_str(); const char* empty = nullptr; const char* args[] = {file_arg, empty}; SBLaunchInfo launch_info (args); return Launch (launch_info); } void WriteResults (Results &results) { // Gotta turn off the last timer now. m_individual_step_times.push_back(m_time_measurements.Stop()); size_t num_time_measurements = m_individual_step_times.size(); Results::Dictionary& results_dict = results.GetDictionary(); const char *short_format_string = "step-time-%0.2d"; const size_t short_size = strlen(short_format_string) + 5; char short_buffer[short_size]; const char *long_format_string = "The time it takes for step %d in the step sequence."; const size_t long_size = strlen(long_format_string) + 5; char long_buffer[long_size]; for (size_t i = 0; i < num_time_measurements; i++) { snprintf (short_buffer, short_size, short_format_string, i); snprintf (long_buffer, long_size, long_format_string, i); results_dict.AddDouble(short_buffer, long_buffer, m_individual_step_times[i]); } results_dict.AddDouble ("total-time", "Total time spent stepping.", m_time_measurements.GetMetric().GetSum()); results_dict.AddDouble ("stddev-time", "StdDev of time spent stepping.", m_time_measurements.GetMetric().GetStandardDeviation()); results.Write(m_out_path.c_str()); } const char * GetExecutablePath () const { if (m_app_path.empty()) return NULL; return m_app_path.c_str(); } const char * GetResultFilePath () const { if (m_out_path.empty()) return NULL; return m_out_path.c_str(); } void SetExecutablePath (const char *path) { if (path && path[0]) m_app_path = path; else m_app_path.clear(); } void SetResultFilePath (const char *path) { if (path && path[0]) m_out_path = path; else m_out_path.clear(); } void SetUseSingleStep (bool use_it) { m_use_single_stepping = use_it; } private: virtual void TestStep (int counter, ActionWanted &next_action) { if (counter > 0) { m_individual_step_times.push_back(m_time_measurements.Stop()); } // Disable the breakpoint, just in case it gets multiple locations we don't want that confusing the stepping. if (counter == 0) m_first_bp.SetEnabled(false); next_action.StepOver(m_process.GetThreadAtIndex(0)); m_time_measurements.Start(); } SBBreakpoint m_first_bp; SBFileSpec m_main_source; TimeMeasurement m_time_measurements; std::vector m_individual_step_times; bool m_use_single_stepping; std::string m_app_path; std::string m_out_path; }; struct Options { std::string test_file_path; std::string out_file; bool verbose; bool fast_step; bool error; bool print_help; Options() : verbose (false), fast_step (true), error (false), print_help (false) { } }; static struct option g_long_options[] = { { "verbose", no_argument, NULL, 'v' }, { "single-step", no_argument, NULL, 's' }, { "test-file", required_argument, NULL, 't' }, { "out-file", required_argument, NULL, 'o' }, { NULL, 0, NULL, 0 } }; std::string GetShortOptionString (struct option *long_options) { std::string option_string; for (int i = 0; long_options[i].name != NULL; ++i) { if (long_options[i].flag == NULL) { option_string.push_back ((char) long_options[i].val); switch (long_options[i].has_arg) { default: case no_argument: break; case required_argument: option_string.push_back (':'); break; case optional_argument: option_string.append (2, ':'); break; } } } return option_string; } int main(int argc, const char * argv[]) { // Prepare for & make calls to getopt_long_only. std::string short_option_string (GetShortOptionString(g_long_options)); StepTest test; Options option_data; bool done = false; #if __GLIBC__ optind = 0; #else optreset = 1; optind = 1; #endif while (!done) { int long_options_index = -1; const int short_option = ::getopt_long_only (argc, const_cast(argv), short_option_string.c_str(), g_long_options, &long_options_index); switch (short_option) { case 0: // Already handled break; case -1: done = true; break; case '?': option_data.print_help = true; break; case 'h': option_data.print_help = true; break; case 'v': option_data.verbose = true; break; case 's': option_data.fast_step = false; test.SetUseSingleStep(true); break; case 't': { SBFileSpec file(optarg); if (file.Exists()) test.SetExecutablePath(optarg); else fprintf(stderr, "error: file specified in --test-file (-t) option doesn't exist: '%s'\n", optarg); } break; case 'o': test.SetResultFilePath(optarg); break; default: option_data.error = true; option_data.print_help = true; fprintf (stderr, "error: unrecognized option %c\n", short_option); break; } } if (option_data.print_help) { puts(R"( NAME lldb-perf-stepping -- a tool that measures LLDB peformance of simple stepping operations. SYNOPSIS lldb-perf-stepping --test-file=FILE [--out-file=PATH --verbose --fast-step] DESCRIPTION Runs a set of stepping operations, timing each step and outputs results to a plist file. )"); exit(0); } if (option_data.error) { exit(1); } if (test.GetExecutablePath() == NULL) { // --clang is mandatory option_data.print_help = true; option_data.error = true; fprintf (stderr, "error: the '--test-file=PATH' option is mandatory\n"); } // Update argc and argv after parsing options argc -= optind; argv += optind; test.SetVerbose(true); TestCase::Run(test, argc, argv); return 0; }