//===-- 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 #include "lldb-perf/lib/Measurement.h" #include "lldb-perf/lib/Metric.h" #include "lldb-perf/lib/TestCase.h" #include "lldb-perf/lib/Timer.h" #include "lldb-perf/lib/Xcode.h" #include #include #include #include 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> m_fetch_frames_measurement; Measurement> m_file_line_bp_measurement; Measurement> m_fetch_modules_measurement; Measurement> m_fetch_vars_measurement; Measurement> 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); }