Home | History | Annotate | Download | only in stepping
      1 #include <CoreFoundation/CoreFoundation.h>
      2 
      3 #include "lldb-perf/lib/Timer.h"
      4 #include "lldb-perf/lib/Metric.h"
      5 #include "lldb-perf/lib/Measurement.h"
      6 #include "lldb-perf/lib/TestCase.h"
      7 #include "lldb-perf/lib/Xcode.h"
      8 
      9 #include <unistd.h>
     10 #include <string>
     11 #include <getopt.h>
     12 
     13 using namespace lldb_perf;
     14 
     15 class StepTest : public TestCase
     16 {
     17     typedef void (*no_function) (void);
     18 
     19 public:
     20     StepTest(bool use_single_stepping = false) :
     21         m_main_source("stepping-testcase.cpp"),
     22         m_use_single_stepping(use_single_stepping),
     23         m_time_measurements(nullptr)
     24     {
     25     }
     26 
     27     virtual
     28     ~StepTest() {}
     29 
     30     virtual bool
     31     Setup (int& argc, const char**& argv)
     32     {
     33         TestCase::Setup (argc, argv);
     34 
     35         // Toggle the fast stepping command on or off as required.
     36         const char *single_step_cmd = "settings set target.use-fast-stepping false";
     37         const char *fast_step_cmd   = "settings set target.use-fast-stepping true";
     38         const char *cmd_to_use;
     39 
     40         if (m_use_single_stepping)
     41             cmd_to_use = single_step_cmd;
     42         else
     43             cmd_to_use = fast_step_cmd;
     44 
     45         SBCommandReturnObject return_object;
     46         m_debugger.GetCommandInterpreter().HandleCommand(cmd_to_use,
     47                                                          return_object);
     48         if (!return_object.Succeeded())
     49         {
     50             if (return_object.GetError() != NULL)
     51                 printf ("Got an error running settings set: %s.\n", return_object.GetError());
     52             else
     53                 printf ("Failed running settings set, no error.\n");
     54         }
     55 
     56         m_target = m_debugger.CreateTarget(m_app_path.c_str());
     57         m_first_bp = m_target.BreakpointCreateBySourceRegex("Here is some code to stop at originally.", m_main_source);
     58 
     59         const char* file_arg = m_app_path.c_str();
     60         const char* empty = nullptr;
     61         const char* args[] = {file_arg, empty};
     62         SBLaunchInfo launch_info (args);
     63 
     64         return Launch (launch_info);
     65     }
     66 
     67     void
     68     WriteResults (Results &results)
     69     {
     70         // Gotta turn off the last timer now.
     71         m_individual_step_times.push_back(m_time_measurements.Stop());
     72 
     73         size_t num_time_measurements = m_individual_step_times.size();
     74 
     75         Results::Dictionary& results_dict = results.GetDictionary();
     76         const char *short_format_string = "step-time-%0.2d";
     77         const size_t short_size = strlen(short_format_string) + 5;
     78         char short_buffer[short_size];
     79         const char *long_format_string  = "The time it takes for step %d in the step sequence.";
     80         const size_t long_size = strlen(long_format_string) + 5;
     81         char long_buffer[long_size];
     82 
     83         for (size_t i = 0; i < num_time_measurements; i++)
     84         {
     85             snprintf (short_buffer, short_size, short_format_string, i);
     86             snprintf (long_buffer, long_size, long_format_string, i);
     87 
     88             results_dict.AddDouble(short_buffer,
     89                                    long_buffer,
     90                                    m_individual_step_times[i]);
     91 
     92         }
     93         results_dict.AddDouble ("total-time", "Total time spent stepping.", m_time_measurements.GetMetric().GetSum());
     94         results_dict.AddDouble ("stddev-time", "StdDev of time spent stepping.", m_time_measurements.GetMetric().GetStandardDeviation());
     95 
     96         results.Write(m_out_path.c_str());
     97     }
     98 
     99 
    100     const char *
    101     GetExecutablePath () const
    102     {
    103         if (m_app_path.empty())
    104             return NULL;
    105         return m_app_path.c_str();
    106     }
    107 
    108     const char *
    109     GetResultFilePath () const
    110     {
    111         if (m_out_path.empty())
    112             return NULL;
    113         return m_out_path.c_str();
    114     }
    115 
    116     void
    117     SetExecutablePath (const char *path)
    118     {
    119         if (path && path[0])
    120             m_app_path = path;
    121         else
    122             m_app_path.clear();
    123     }
    124 
    125     void
    126     SetResultFilePath (const char *path)
    127     {
    128         if (path && path[0])
    129             m_out_path = path;
    130         else
    131             m_out_path.clear();
    132     }
    133 
    134     void
    135     SetUseSingleStep (bool use_it)
    136     {
    137         m_use_single_stepping = use_it;
    138     }
    139 private:
    140     virtual void
    141 	TestStep (int counter, ActionWanted &next_action)
    142     {
    143         if (counter > 0)
    144         {
    145             m_individual_step_times.push_back(m_time_measurements.Stop());
    146 
    147         }
    148 
    149         // Disable the breakpoint, just in case it gets multiple locations we don't want that confusing the stepping.
    150         if (counter == 0)
    151             m_first_bp.SetEnabled(false);
    152 
    153         next_action.StepOver(m_process.GetThreadAtIndex(0));
    154         m_time_measurements.Start();
    155 
    156 
    157     }
    158 
    159     SBBreakpoint m_first_bp;
    160     SBFileSpec   m_main_source;
    161     TimeMeasurement<no_function> m_time_measurements;
    162     std::vector<double>          m_individual_step_times;
    163     bool m_use_single_stepping;
    164     std::string m_app_path;
    165     std::string m_out_path;
    166 
    167 
    168 };
    169 
    170 struct Options
    171 {
    172     std::string test_file_path;
    173     std::string out_file;
    174     bool verbose;
    175     bool fast_step;
    176     bool error;
    177     bool print_help;
    178 
    179     Options() :
    180         verbose (false),
    181         fast_step (true),
    182         error (false),
    183         print_help (false)
    184     {
    185     }
    186 };
    187 
    188 static struct option g_long_options[] = {
    189     { "verbose",      no_argument,            NULL, 'v' },
    190     { "single-step",  no_argument,            NULL, 's' },
    191     { "test-file",    required_argument,      NULL, 't' },
    192     { "out-file",     required_argument,      NULL, 'o' },
    193     { NULL,           0,                      NULL,  0  }
    194 };
    195 
    196 
    197 std::string
    198 GetShortOptionString (struct option *long_options)
    199 {
    200     std::string option_string;
    201     for (int i = 0; long_options[i].name != NULL; ++i)
    202     {
    203         if (long_options[i].flag == NULL)
    204         {
    205             option_string.push_back ((char) long_options[i].val);
    206             switch (long_options[i].has_arg)
    207             {
    208                 default:
    209                 case no_argument:
    210                     break;
    211                 case required_argument:
    212                     option_string.push_back (':');
    213                     break;
    214                 case optional_argument:
    215                     option_string.append (2, ':');
    216                     break;
    217             }
    218         }
    219     }
    220     return option_string;
    221 }
    222 
    223 int main(int argc, const char * argv[])
    224 {
    225 
    226     // Prepare for & make calls to getopt_long_only.
    227 
    228     std::string short_option_string (GetShortOptionString(g_long_options));
    229 
    230     StepTest test;
    231 
    232     Options option_data;
    233     bool done = false;
    234 
    235 #if __GLIBC__
    236     optind = 0;
    237 #else
    238     optreset = 1;
    239     optind = 1;
    240 #endif
    241     while (!done)
    242     {
    243         int long_options_index = -1;
    244         const int short_option = ::getopt_long_only (argc,
    245                                                      const_cast<char **>(argv),
    246                                                      short_option_string.c_str(),
    247                                                      g_long_options,
    248                                                      &long_options_index);
    249 
    250         switch (short_option)
    251         {
    252             case 0:
    253                 // Already handled
    254                 break;
    255 
    256             case -1:
    257                 done = true;
    258                 break;
    259 
    260             case '?':
    261                 option_data.print_help = true;
    262                 break;
    263 
    264             case 'h':
    265                 option_data.print_help = true;
    266                 break;
    267 
    268             case 'v':
    269                 option_data.verbose = true;
    270                 break;
    271 
    272             case 's':
    273                 option_data.fast_step = false;
    274                 test.SetUseSingleStep(true);
    275                 break;
    276 
    277             case 't':
    278                 {
    279                     SBFileSpec file(optarg);
    280                     if (file.Exists())
    281                         test.SetExecutablePath(optarg);
    282                     else
    283                         fprintf(stderr, "error: file specified in --test-file (-t) option doesn't exist: '%s'\n", optarg);
    284                 }
    285                 break;
    286 
    287             case 'o':
    288                 test.SetResultFilePath(optarg);
    289                 break;
    290 
    291             default:
    292                 option_data.error = true;
    293                 option_data.print_help = true;
    294                 fprintf (stderr, "error: unrecognized option %c\n", short_option);
    295                 break;
    296         }
    297     }
    298 
    299 
    300     if (option_data.print_help)
    301     {
    302         puts(R"(
    303 NAME
    304     lldb-perf-stepping -- a tool that measures LLDB peformance of simple stepping operations.
    305 
    306 SYNOPSIS
    307     lldb-perf-stepping --test-file=FILE [--out-file=PATH --verbose --fast-step]
    308 
    309 DESCRIPTION
    310     Runs a set of stepping operations, timing each step and outputs results
    311     to a plist file.
    312 )");
    313         exit(0);
    314     }
    315     if (option_data.error)
    316     {
    317         exit(1);
    318     }
    319 
    320     if (test.GetExecutablePath() == NULL)
    321     {
    322         // --clang is mandatory
    323         option_data.print_help = true;
    324         option_data.error = true;
    325         fprintf (stderr, "error: the '--test-file=PATH' option is mandatory\n");
    326     }
    327 
    328     // Update argc and argv after parsing options
    329     argc -= optind;
    330     argv += optind;
    331 
    332     test.SetVerbose(true);
    333     TestCase::Run(test, argc, argv);
    334     return 0;
    335 }
    336