| //===-- sketch.cpp ----------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include <CoreFoundation/CoreFoundation.h> |
| |
| #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 <iostream> |
| #include <unistd.h> |
| #include <fstream> |
| #include <getopt.h> |
| |
| using namespace lldb_perf; |
| |
| static struct option g_long_options[] = { |
| { "verbose", no_argument, NULL, 'v' }, |
| { "sketch", required_argument, NULL, 'c' }, |
| { "foobar", required_argument, NULL, 'f' }, |
| { "out-file", required_argument, NULL, 'o' }, |
| { NULL, 0, NULL, 0 } |
| }; |
| |
| class SketchTest : public TestCase |
| { |
| public: |
| SketchTest () : |
| m_fetch_frames_measurement ([this] () -> void |
| { |
| Xcode::FetchFrames (GetProcess(),false,false); |
| }, "fetch-frames", "time to dump backtrace for every frame in every thread"), |
| m_file_line_bp_measurement([this] (const char* file, uint32_t line) -> void |
| { |
| Xcode::CreateFileLineBreakpoint(GetTarget(), file, line); |
| }, "file-line-bkpt", "time to set a breakpoint given a file and line"), |
| m_fetch_modules_measurement ([this] () -> void |
| { |
| Xcode::FetchModules(GetTarget()); |
| }, "fetch-modules", "time to get info for all modules in the process"), |
| m_fetch_vars_measurement([this] (int depth) -> void |
| { |
| SBProcess process (GetProcess()); |
| auto threads_count = process.GetNumThreads(); |
| for (size_t thread_num = 0; thread_num < threads_count; thread_num++) |
| { |
| SBThread thread(process.GetThreadAtIndex(thread_num)); |
| SBFrame frame(thread.GetFrameAtIndex(0)); |
| Xcode::FetchVariables(frame,depth,GetVerbose()); |
| } |
| }, "fetch-vars", "time to dump variables for the topmost frame in every thread"), |
| m_run_expr_measurement([this] (SBFrame frame, const char* expr) -> void |
| { |
| SBValue value(frame.EvaluateExpression(expr, lldb::eDynamicCanRunTarget)); |
| Xcode::FetchVariable (value, 0, GetVerbose()); |
| }, "run-expr", "time to evaluate an expression and display the result") |
| { |
| m_app_path.clear(); |
| m_out_path.clear(); |
| m_doc_path.clear(); |
| m_print_help = false; |
| } |
| |
| virtual |
| ~SketchTest () |
| { |
| } |
| |
| virtual bool |
| ParseOption (int short_option, const char* optarg) |
| { |
| switch (short_option) |
| { |
| case 0: |
| return false; |
| |
| case -1: |
| return false; |
| |
| case '?': |
| case 'h': |
| m_print_help = true; |
| break; |
| |
| case 'v': |
| SetVerbose(true); |
| break; |
| |
| case 'c': |
| { |
| SBFileSpec file(optarg); |
| if (file.Exists()) |
| SetExecutablePath(optarg); |
| else |
| fprintf(stderr, "error: file specified in --sketch (-c) option doesn't exist: '%s'\n", optarg); |
| } |
| break; |
| |
| case 'f': |
| { |
| SBFileSpec file(optarg); |
| if (file.Exists()) |
| SetDocumentPath(optarg); |
| else |
| fprintf(stderr, "error: file specified in --foobar (-f) option doesn't exist: '%s'\n", optarg); |
| } |
| break; |
| |
| case 'o': |
| SetResultFilePath(optarg); |
| break; |
| |
| default: |
| m_print_help = true; |
| fprintf (stderr, "error: unrecognized option %c\n", short_option); |
| break; |
| } |
| return true; |
| } |
| |
| virtual struct option* |
| GetLongOptions () |
| { |
| return g_long_options; |
| } |
| |
| virtual bool |
| Setup (int& argc, const char**& argv) |
| { |
| TestCase::Setup(argc,argv); |
| bool error = false; |
| |
| if (GetExecutablePath() == NULL) |
| { |
| // --sketch is mandatory |
| error = true; |
| fprintf (stderr, "error: the '--sketch=PATH' option is mandatory\n"); |
| } |
| |
| if (GetDocumentPath() == NULL) |
| { |
| // --foobar is mandatory |
| error = true; |
| fprintf (stderr, "error: the '--foobar=PATH' option is mandatory\n"); |
| } |
| |
| if (error || GetPrintHelp()) |
| { |
| puts(R"( |
| NAME |
| lldb_perf_sketch -- a tool that measures LLDB peformance while debugging sketch. |
| |
| SYNOPSIS |
| lldb_perf_sketch --sketch=PATH --foobar=PATH [--out-file=PATH --verbose] |
| |
| DESCRIPTION |
| Runs a set of static timing and memory tasks against sketch and outputs results |
| to a plist file. |
| )"); |
| } |
| |
| if (error) |
| { |
| exit(1); |
| } |
| lldb::SBLaunchInfo launch_info = GetLaunchInfo(); |
| m_target = m_debugger.CreateTarget(m_app_path.c_str()); |
| m_file_line_bp_measurement("SKTDocument.m",245); |
| m_file_line_bp_measurement("SKTDocument.m",283); |
| m_file_line_bp_measurement("SKTText.m",326); |
| return Launch (launch_info); |
| } |
| |
| lldb::SBLaunchInfo |
| GetLaunchInfo () |
| { |
| const char* file_arg = m_doc_path.c_str(); |
| const char* persist_arg = "-ApplePersistenceIgnoreState"; |
| const char* persist_skip = "YES"; |
| const char* empty = nullptr; |
| const char* args[] = {file_arg,persist_arg,persist_skip,empty}; |
| return SBLaunchInfo(args); |
| } |
| |
| void |
| DoTest () |
| { |
| m_fetch_frames_measurement(); |
| m_fetch_modules_measurement(); |
| m_fetch_vars_measurement(1); |
| } |
| |
| virtual void |
| TestStep (int counter, ActionWanted &next_action) |
| { |
| static int launch = 1; |
| switch (counter % 10) |
| { |
| case 0: |
| { |
| DoTest (); |
| if (counter == 0) |
| m_file_line_bp_measurement("SKTDocument.m",254); |
| next_action.Continue(); |
| } |
| break; |
| |
| case 1: |
| { |
| DoTest (); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"properties"); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[properties description]"); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"typeName"); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"data"); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[data description]"); |
| next_action.Continue(); |
| } |
| break; |
| |
| case 2: |
| { |
| DoTest (); |
| next_action.Continue(); |
| } |
| break; |
| |
| case 3: |
| { |
| DoTest (); |
| next_action.StepOver(m_thread); |
| } |
| break; |
| |
| case 4: |
| { |
| DoTest (); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"layoutManager"); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"contents"); |
| next_action.StepOver(m_thread); |
| } |
| break; |
| |
| case 5: |
| { |
| DoTest (); |
| next_action.StepOver(m_thread); |
| } |
| break; |
| |
| case 6: |
| { |
| DoTest (); |
| next_action.StepOver(m_thread); |
| } |
| break; |
| |
| case 7: |
| { |
| DoTest (); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"@\"an NSString\""); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[(id)@\"an NSString\" description]"); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"@[@1,@2,@3]"); |
| next_action.StepOut(m_thread); |
| } |
| break; |
| |
| case 8: |
| { |
| DoTest (); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[graphics description]"); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[selectionIndexes description]"); |
| m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"(BOOL)NSIntersectsRect(rect, graphicDrawingBounds)"); |
| } |
| next_action.CallNext(); |
| break; |
| case 9: |
| if (++launch < 10) |
| next_action.Relaunch(GetLaunchInfo()); |
| else |
| next_action.Kill(); |
| break; |
| |
| |
| default: |
| { |
| next_action.Kill(); |
| } |
| break; |
| } |
| } |
| |
| virtual void |
| WriteResults (Results &results) |
| { |
| m_fetch_frames_measurement.WriteAverageAndStandardDeviation(results); |
| m_file_line_bp_measurement.WriteAverageAndStandardDeviation(results); |
| m_fetch_modules_measurement.WriteAverageAndStandardDeviation(results); |
| m_fetch_vars_measurement.WriteAverageAndStandardDeviation(results); |
| m_run_expr_measurement.WriteAverageAndStandardDeviation(results); |
| results.Write(GetResultFilePath()); |
| } |
| |
| void |
| SetExecutablePath (const char* str) |
| { |
| if (str) |
| m_app_path.assign(str); |
| } |
| |
| const char* |
| GetExecutablePath () |
| { |
| if (m_app_path.empty()) |
| return NULL; |
| return m_app_path.c_str(); |
| } |
| |
| void |
| SetDocumentPath (const char* str) |
| { |
| if (str) |
| m_doc_path.assign(str); |
| } |
| |
| const char* |
| GetDocumentPath () |
| { |
| if (m_doc_path.empty()) |
| return NULL; |
| return m_doc_path.c_str(); |
| } |
| |
| |
| void |
| SetResultFilePath (const char* str) |
| { |
| if (str) |
| m_out_path.assign(str); |
| } |
| |
| const char* |
| GetResultFilePath () |
| { |
| if (m_out_path.empty()) |
| return "/dev/stdout"; |
| return m_out_path.c_str(); |
| } |
| |
| bool |
| GetPrintHelp () |
| { |
| return m_print_help; |
| } |
| |
| private: |
| Measurement<lldb_perf::TimeGauge, std::function<void()>> m_fetch_frames_measurement; |
| Measurement<lldb_perf::TimeGauge, std::function<void(const char*, uint32_t)>> m_file_line_bp_measurement; |
| Measurement<lldb_perf::TimeGauge, std::function<void()>> m_fetch_modules_measurement; |
| Measurement<lldb_perf::TimeGauge, std::function<void(int)>> m_fetch_vars_measurement; |
| Measurement<lldb_perf::TimeGauge, std::function<void(SBFrame, const char*)>> m_run_expr_measurement; |
| |
| std::string m_app_path; |
| std::string m_doc_path; |
| std::string m_out_path; |
| bool m_print_help; |
| }; |
| |
| int main(int argc, const char * argv[]) |
| { |
| SketchTest test; |
| return TestCase::Run(test, argc, argv); |
| } |