1//===-- sketch.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 <CoreFoundation/CoreFoundation.h>
11
12#include "lldb-perf/lib/Timer.h"
13#include "lldb-perf/lib/Metric.h"
14#include "lldb-perf/lib/Measurement.h"
15#include "lldb-perf/lib/TestCase.h"
16#include "lldb-perf/lib/Xcode.h"
17
18#include <iostream>
19#include <unistd.h>
20#include <fstream>
21#include <getopt.h>
22
23using namespace lldb_perf;
24
25static struct option g_long_options[] = {
26    { "verbose",    no_argument,            NULL, 'v' },
27    { "sketch",     required_argument,      NULL, 'c' },
28    { "foobar",     required_argument,      NULL, 'f' },
29    { "out-file",   required_argument,      NULL, 'o' },
30    { NULL,         0,                      NULL,  0  }
31};
32
33class SketchTest : public TestCase
34{
35public:
36    SketchTest () :
37        m_fetch_frames_measurement ([this] () -> void
38            {
39                Xcode::FetchFrames (GetProcess(),false,false);
40            }, "fetch-frames", "time to dump backtrace for every frame in every thread"),
41        m_file_line_bp_measurement([this] (const char* file, uint32_t line) -> void
42            {
43                Xcode::CreateFileLineBreakpoint(GetTarget(), file, line);
44            }, "file-line-bkpt", "time to set a breakpoint given a file and line"),
45        m_fetch_modules_measurement ([this] () -> void
46            {
47                Xcode::FetchModules(GetTarget());
48            }, "fetch-modules", "time to get info for all modules in the process"),
49        m_fetch_vars_measurement([this] (int depth) -> void
50            {
51                SBProcess process (GetProcess());
52                auto threads_count = process.GetNumThreads();
53                for (size_t thread_num = 0; thread_num < threads_count; thread_num++)
54                {
55                    SBThread thread(process.GetThreadAtIndex(thread_num));
56                    SBFrame frame(thread.GetFrameAtIndex(0));
57                    Xcode::FetchVariables(frame,depth,GetVerbose());
58                }
59            }, "fetch-vars", "time to dump variables for the topmost frame in every thread"),
60        m_run_expr_measurement([this] (SBFrame frame, const char* expr) -> void
61            {
62                SBValue value(frame.EvaluateExpression(expr, lldb::eDynamicCanRunTarget));
63                Xcode::FetchVariable (value, 0, GetVerbose());
64            }, "run-expr", "time to evaluate an expression and display the result")
65    {
66        m_app_path.clear();
67        m_out_path.clear();
68        m_doc_path.clear();
69        m_print_help = false;
70    }
71
72    virtual
73    ~SketchTest ()
74    {
75    }
76
77    virtual bool
78    ParseOption (int short_option, const char* optarg)
79    {
80        switch (short_option)
81        {
82            case 0:
83                return false;
84
85            case -1:
86                return false;
87
88            case '?':
89            case 'h':
90                m_print_help = true;
91                break;
92
93            case 'v':
94                SetVerbose(true);
95                break;
96
97            case 'c':
98            {
99                SBFileSpec file(optarg);
100                if (file.Exists())
101                    SetExecutablePath(optarg);
102                else
103                    fprintf(stderr, "error: file specified in --sketch (-c) option doesn't exist: '%s'\n", optarg);
104            }
105                break;
106
107            case 'f':
108            {
109                SBFileSpec file(optarg);
110                if (file.Exists())
111                    SetDocumentPath(optarg);
112                else
113                    fprintf(stderr, "error: file specified in --foobar (-f) option doesn't exist: '%s'\n", optarg);
114            }
115                break;
116
117            case 'o':
118                SetResultFilePath(optarg);
119                break;
120
121            default:
122                m_print_help = true;
123                fprintf (stderr, "error: unrecognized option %c\n", short_option);
124                break;
125        }
126        return true;
127    }
128
129    virtual struct option*
130    GetLongOptions ()
131    {
132        return g_long_options;
133    }
134
135    virtual bool
136	Setup (int& argc, const char**& argv)
137    {
138        TestCase::Setup(argc,argv);
139        bool error = false;
140
141        if (GetExecutablePath() == NULL)
142        {
143            // --sketch is mandatory
144            error = true;
145            fprintf (stderr, "error: the '--sketch=PATH' option is mandatory\n");
146        }
147
148        if (GetDocumentPath() == NULL)
149        {
150            // --foobar is mandatory
151            error = true;
152            fprintf (stderr, "error: the '--foobar=PATH' option is mandatory\n");
153        }
154
155        if (error || GetPrintHelp())
156        {
157            puts(R"(
158                 NAME
159                 lldb_perf_sketch -- a tool that measures LLDB peformance while debugging sketch.
160
161                 SYNOPSIS
162                 lldb_perf_sketch --sketch=PATH --foobar=PATH [--out-file=PATH --verbose]
163
164                 DESCRIPTION
165                 Runs a set of static timing and memory tasks against sketch and outputs results
166                 to a plist file.
167                 )");
168        }
169
170        if (error)
171        {
172            exit(1);
173        }
174        lldb::SBLaunchInfo launch_info = GetLaunchInfo();
175        m_target = m_debugger.CreateTarget(m_app_path.c_str());
176        m_file_line_bp_measurement("SKTDocument.m",245);
177        m_file_line_bp_measurement("SKTDocument.m",283);
178        m_file_line_bp_measurement("SKTText.m",326);
179        return Launch (launch_info);
180    }
181
182    lldb::SBLaunchInfo
183    GetLaunchInfo ()
184    {
185        const char* file_arg = m_doc_path.c_str();
186        const char* persist_arg = "-ApplePersistenceIgnoreState";
187        const char* persist_skip = "YES";
188        const char* empty = nullptr;
189        const char* args[] = {file_arg,persist_arg,persist_skip,empty};
190        return SBLaunchInfo(args);
191    }
192
193    void
194    DoTest ()
195    {
196        m_fetch_frames_measurement();
197        m_fetch_modules_measurement();
198        m_fetch_vars_measurement(1);
199    }
200
201	virtual void
202	TestStep (int counter, ActionWanted &next_action)
203    {
204        static int launch = 1;
205        switch (counter % 10)
206        {
207        case 0:
208            {
209                DoTest ();
210                if (counter == 0)
211                    m_file_line_bp_measurement("SKTDocument.m",254);
212                next_action.Continue();
213            }
214            break;
215
216        case 1:
217            {
218                DoTest ();
219                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"properties");
220                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[properties description]");
221                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"typeName");
222                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"data");
223                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[data description]");
224                next_action.Continue();
225            }
226            break;
227
228        case 2:
229            {
230                DoTest ();
231                next_action.Continue();
232            }
233            break;
234
235        case 3:
236            {
237                DoTest ();
238                next_action.StepOver(m_thread);
239            }
240            break;
241
242        case 4:
243            {
244                DoTest ();
245                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"layoutManager");
246                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"contents");
247                next_action.StepOver(m_thread);
248            }
249            break;
250
251        case 5:
252            {
253                DoTest ();
254                next_action.StepOver(m_thread);
255            }
256            break;
257
258        case 6:
259            {
260                DoTest ();
261                next_action.StepOver(m_thread);
262            }
263            break;
264
265        case 7:
266            {
267                DoTest ();
268                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"@\"an NSString\"");
269                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[(id)@\"an NSString\" description]");
270                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"@[@1,@2,@3]");
271                next_action.StepOut(m_thread);
272            }
273            break;
274
275        case 8:
276            {
277                DoTest ();
278                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[graphics description]");
279                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"[selectionIndexes description]");
280                m_run_expr_measurement(m_thread.GetFrameAtIndex(0),"(BOOL)NSIntersectsRect(rect, graphicDrawingBounds)");
281            }
282            next_action.CallNext();
283            break;
284        case 9:
285            if (++launch < 10)
286                next_action.Relaunch(GetLaunchInfo());
287            else
288                next_action.Kill();
289            break;
290
291
292        default:
293            {
294                next_action.Kill();
295            }
296            break;
297        }
298    }
299
300    virtual void
301    WriteResults (Results &results)
302    {
303        m_fetch_frames_measurement.WriteAverageAndStandardDeviation(results);
304        m_file_line_bp_measurement.WriteAverageAndStandardDeviation(results);
305        m_fetch_modules_measurement.WriteAverageAndStandardDeviation(results);
306        m_fetch_vars_measurement.WriteAverageAndStandardDeviation(results);
307        m_run_expr_measurement.WriteAverageAndStandardDeviation(results);
308        results.Write(GetResultFilePath());
309    }
310
311    void
312    SetExecutablePath (const char* str)
313    {
314        if (str)
315            m_app_path.assign(str);
316    }
317
318    const char*
319    GetExecutablePath ()
320    {
321        if (m_app_path.empty())
322            return NULL;
323        return m_app_path.c_str();
324    }
325
326    void
327    SetDocumentPath (const char* str)
328    {
329        if (str)
330            m_doc_path.assign(str);
331    }
332
333    const char*
334    GetDocumentPath ()
335    {
336        if (m_doc_path.empty())
337            return NULL;
338        return m_doc_path.c_str();
339    }
340
341
342    void
343    SetResultFilePath (const char* str)
344    {
345        if (str)
346            m_out_path.assign(str);
347    }
348
349    const char*
350    GetResultFilePath ()
351    {
352        if (m_out_path.empty())
353            return "/dev/stdout";
354        return m_out_path.c_str();
355    }
356
357    bool
358    GetPrintHelp ()
359    {
360        return m_print_help;
361    }
362
363private:
364    Measurement<lldb_perf::TimeGauge, std::function<void()>> m_fetch_frames_measurement;
365    Measurement<lldb_perf::TimeGauge, std::function<void(const char*, uint32_t)>> m_file_line_bp_measurement;
366    Measurement<lldb_perf::TimeGauge, std::function<void()>> m_fetch_modules_measurement;
367    Measurement<lldb_perf::TimeGauge, std::function<void(int)>> m_fetch_vars_measurement;
368    Measurement<lldb_perf::TimeGauge, std::function<void(SBFrame, const char*)>> m_run_expr_measurement;
369
370    std::string m_app_path;
371    std::string m_doc_path;
372    std::string m_out_path;
373    bool m_print_help;
374};
375
376int main(int argc, const char * argv[])
377{
378    SketchTest test;
379    return TestCase::Run(test, argc, argv);
380}
381