Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,295 @@
The lldb-perf infrastructure for LLDB performance testing
===========================================================
lldb-perf is an infrastructure meant to simplify the creation of performance
tests for the LLDB debugger. It is contained in liblldbperf.a which is part of
the standard opensource checkout of LLDB
Its main concepts are:
- Gauges: a gauge is a thing that takes a sample. Samples include elapsed time,
memory used, and energy consumed.
- Metrics: a metric is a collection of samples that knows how to do statistics
like sum() and average(). Metrics can be extended as needed.
- Measurements: a measurement is the thing that stores an action, a gauge and
a metric. You define measurements as in “take the time to run this function”,
“take the memory to run this block of code”, and then after you invoke it,
your stats will automagically be there.
- Tests: a test is a sequence of steps and measurements.
Tests cases should be added as targets to the lldbperf.xcodeproj project. It
is probably easiest to duplicate one of the existing targets. In order to
write a test based on lldb-perf, you need to subclass lldb_perf::TestCase:
using namespace lldb_perf;
class FormattersTest : public TestCase
{
Usually, you will define measurements as variables of your test case class:
private:
// C++ formatters
TimeMeasurement<std::function<void(SBValue)>> m_dump_std_vector_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_std_list_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_std_map_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_std_string_measurement;
// Cocoa formatters
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsstring_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsarray_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsdictionary_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsset_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsbundle_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsdate_measurement;
A TimeMeasurement is, obviously, a class that measures “how much time to run
this block of code”. The block of code is passed as an std::function which you
can construct with a lambda! You need to give the prototype of your block of
code. In this example, we run blocks of code that take an SBValue and return
nothing.
These blocks look like:
m_dump_std_vector_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,1,false);
}, "std-vector", "time to dump an std::vector");
Here we are saying: make me a measurement named “std-vector”, whose
description is “time to dump an std::vector” and that takes the time required
to call lldb_perf::Xcode::FetchVariable(value,1,false).
The Xcode class is a collection of utility functions that replicate common
Xcode patterns (FetchVariable unsurprisingly calls API functions that Xcode
could use when populating a variables view entry - the 1 means “expand 1 level
of depth” and the false means “do not dump the data to stdout”)
A full constructor for a TestCase looks like:
FormattersTest () : TestCase()
{
m_dump_std_vector_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,1,false);
}, "std-vector", "time to dump an std::vector");
m_dump_std_list_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,1,false);
}, "std-list", "time to dump an std::list");
m_dump_std_map_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,1,false);
}, "std-map", "time to dump an std::map");
m_dump_std_string_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,1,false);
}, "std-string", "time to dump an std::string");
m_dump_nsstring_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,0,false);
}, "ns-string", "time to dump an NSString");
m_dump_nsarray_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,1,false);
}, "ns-array", "time to dump an NSArray");
m_dump_nsdictionary_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,1,false);
}, "ns-dictionary", "time to dump an NSDictionary");
m_dump_nsset_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,1,false);
}, "ns-set", "time to dump an NSSet");
m_dump_nsbundle_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,1,false);
}, "ns-bundle", "time to dump an NSBundle");
m_dump_nsdate_measurement = CreateTimeMeasurement([] (SBValue value) -> void {
lldb_perf::Xcode::FetchVariable (value,0,false);
}, "ns-date", "time to dump an NSDate");
}
Once your test case is constructed, Setup() is called on it:
virtual bool
Setup (int argc, const char** argv)
{
m_app_path.assign(argv[1]);
m_out_path.assign(argv[2]);
m_target = m_debugger.CreateTarget(m_app_path.c_str());
m_target.BreakpointCreateByName("main");
SBLaunchInfo launch_info (argv);
return Launch (launch_info);
}
Setup() returns a boolean value that indicates if setup was successful.
In Setup() you fill out a SBLaunchInfo with any needed settings for launching
your process like arguments, environment variables, working directory, and
much more.
The last thing you want to do in setup is call Launch():
bool
Launch (coSBLaunchInfo &launch_info);
This ensures your target is now alive. Make sure to have a breakpoint created.
Once you launched, the event loop is entered. The event loop waits for stops,
and when it gets one, it calls your test cases TestStep() function:
virtual void
TestStep (int counter, ActionWanted &next_action)
the counter is the step id (a monotonically increasing counter). In TestStep()
you will essentially run your measurements and then return what you want the
driver to do by filling in the ActionWanted object named "next_action".
Possible options are:
- continue process next_action.Continue();
- kill process next_action.Kill();
- Step-out on a thread next_action.StepOut(SBThread)
- step-over on a thread. next_action.StepOver(SBThread)
If you use ActionWanted::Next() or ActionWanted::Finish() you need to specify
a thread to use. By default the TestCase class will select the first thread
that had a stop reason other than eStopReasonNone and place it into the
m_thread member variable of TestCase. This means if your test case hits a
breakpoint or steps, the thread that hit the breakpoint or finished the step
will automatically be selected in the process (m_process) and m_thread will
be set to this thread. If you have one or more threads that will stop with a
reason simultaneously, you will need to find those threads manually by
iterating through the process list and determine what to do next.
For your convenience TestCase has m_debugger, m_target and m_process as member
variables. As state above m_thread will be filled in with the first thread
that has a stop reason.
An example:
virtual void
TestStep (int counter, ActionWanted &next_action)
{
case 0:
m_target.BreakpointCreateByLocation("fmts_tester.mm", 68);
next_action.Continue();
break;
case 1:
DoTest ();
next_action.Continue();
break;
case 2:
DoTest ();
next_action.StepOver(m_thread);
break;
DoTest() is a function I define in my own class that calls the measurements:
void
DoTest ()
{
SBThread thread_main(m_thread);
SBFrame frame_zero(thread_main.GetFrameAtIndex(0));
m_dump_nsarray_measurement(frame_zero.FindVariable("nsarray", lldb::eDynamicCanRunTarget));
m_dump_nsarray_measurement(frame_zero.FindVariable("nsmutablearray", lldb::eDynamicCanRunTarget));
m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsdictionary", lldb::eDynamicCanRunTarget));
m_dump_nsdictionary_measurement(frame_zero.FindVariable("nsmutabledictionary", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(frame_zero.FindVariable("str0", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(frame_zero.FindVariable("str1", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(frame_zero.FindVariable("str2", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(frame_zero.FindVariable("str3", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(frame_zero.FindVariable("str4", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(frame_zero.FindVariable("me", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(frame_zero.FindVariable("cutie", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(frame_zero.FindVariable("mom", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(frame_zero.FindVariable("dad", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(frame_zero.FindVariable("today", lldb::eDynamicCanRunTarget));
m_dump_nsbundle_measurement(frame_zero.FindVariable("bundles", lldb::eDynamicCanRunTarget));
m_dump_nsbundle_measurement(frame_zero.FindVariable("frameworks", lldb::eDynamicCanRunTarget));
m_dump_nsset_measurement(frame_zero.FindVariable("nsset", lldb::eDynamicCanRunTarget));
m_dump_nsset_measurement(frame_zero.FindVariable("nsmutableset", lldb::eDynamicCanRunTarget));
m_dump_std_vector_measurement(frame_zero.FindVariable("vector", lldb::eDynamicCanRunTarget));
m_dump_std_list_measurement(frame_zero.FindVariable("list", lldb::eDynamicCanRunTarget));
m_dump_std_map_measurement(frame_zero.FindVariable("map", lldb::eDynamicCanRunTarget));
m_dump_std_string_measurement(frame_zero.FindVariable("sstr0", lldb::eDynamicCanRunTarget));
m_dump_std_string_measurement(frame_zero.FindVariable("sstr1", lldb::eDynamicCanRunTarget));
m_dump_std_string_measurement(frame_zero.FindVariable("sstr2", lldb::eDynamicCanRunTarget));
m_dump_std_string_measurement(frame_zero.FindVariable("sstr3", lldb::eDynamicCanRunTarget));
m_dump_std_string_measurement(frame_zero.FindVariable("sstr4", lldb::eDynamicCanRunTarget));
}
Essentially, you call your measurements as if they were functions, passing
them arguments and all, and they will do the right thing with gathering stats.
The last step is usually to KILL the inferior and bail out:
virtual ActionWanted
TestStep (int counter)
{
...
case 9:
DoTest ();
next_action.Continue();
break;
case 10:
DoTest ();
next_action.Continue();
break;
default:
next_action.Kill();
break;
}
At the end, you define a Results() function:
void
Results ()
{
CFCMutableArray array;
m_dump_std_vector_measurement.Write(array);
m_dump_std_list_measurement.Write(array);
m_dump_std_map_measurement.Write(array);
m_dump_std_string_measurement.Write(array);
m_dump_nsstring_measurement.Write(array);
m_dump_nsarray_measurement.Write(array);
m_dump_nsdictionary_measurement.Write(array);
m_dump_nsset_measurement.Write(array);
m_dump_nsbundle_measurement.Write(array);
m_dump_nsdate_measurement.Write(array);
CFDataRef xmlData = CFPropertyListCreateData (kCFAllocatorDefault,
array.get(),
kCFPropertyListXMLFormat_v1_0,
0,
NULL);
CFURLRef file = CFURLCreateFromFileSystemRepresentation (NULL,
(const UInt8*)m_out_path.c_str(),
m_out_path.size(),
FALSE);
CFURLWriteDataAndPropertiesToResource(file,xmlData,NULL,NULL);
}
For now, pretty much copy this and just call Write() on all your measurements.
I plan to move this higher in the hierarchy (e.g. make a
TestCase::Write(filename) fairly soon).
Your main() will look like:
int main(int argc, const char * argv[])
{
MyTest test;
TestCase::Run (test, argc, argv);
return 0;
}
If you are debugging your test, before Run() call
test.SetVerbose(true);
Feel free to send any questions and ideas for improvements.

View File

@@ -0,0 +1,33 @@
#!/bin/bash
if [ -d "llvm-build" ]; then
echo "Using existing 'llvm-build' directory..."
else
mkdir llvm-build
fi
cd llvm-build
if [ -d "llvm" ]; then
echo "Using existing 'llvm' directory..."
else
svn co --revision 176809 http://llvm.org/svn/llvm-project/llvm/trunk llvm
( cd llvm/tools ; svn co --revision 176809 http://llvm.org/svn/llvm-project/cfe/trunk clang )
fi
if [ ! -d "build" ]; then
mkdir build
cd build
../llvm/configure --enable-targets=x86_64,arm --build=x86_64-apple-darwin10 --disable-optimized --disable-assertions --enable-libcpp
make -j8 clang-only DEBUG_SYMBOLS=1
rm -rf lib projects runtime unittests utils config.*
( cd ./Debug/bin ; rm -rf ll* clang-check clang-tblgen count diagtool fpcmp macho-dump not opt yaml2obj FileCheck FileUpdate arcmt-test c-arcmt-test c-index-test bugpoint )
( cd ./tools ; rm -rf ll* clang-check clang-tblgen count diagtool fpcmp lto macho-dump not opt yaml2obj FileCheck FileUpdate arcmt-test c-arcmt-test c-index-test bugpoint )
( cd ./tools/clang ; rm -rf lib unittests utils )
( cd ./tools/clang/tools ; rm -rf arcmt-test c-arcmt-test c-index-test clang-check diagtool libclang )
( cd ../llvm ; rm -rf cmake configure docs examples projects *.txt *.TXT autoconf bindings test unittests utils ; find . -type d -name .svn -print0 | xargs -0 rm -rf )
( cd ../llvm/tools ; rm -rf *.txt bugpoint bugpoint-passes ll* lto macho-dump opt gold )
fi

View File

@@ -0,0 +1,442 @@
//===-- lldb_perf_clang.cpp -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb-perf/lib/Measurement.h"
#include "lldb-perf/lib/Metric.h"
#include "lldb-perf/lib/Results.h"
#include "lldb-perf/lib/TestCase.h"
#include "lldb-perf/lib/Timer.h"
#include "lldb-perf/lib/Xcode.h"
#include "llvm/ADT/STLExtras.h"
#include <fstream>
#include <getopt.h>
#include <iostream>
#include <unistd.h>
using namespace lldb_perf;
#define NUM_EXPR_ITERATIONS 3
class ClangTest : public TestCase {
public:
ClangTest()
: TestCase(),
m_time_create_target(
[this]() -> void {
m_memory_change_create_target.Start();
m_target = m_debugger.CreateTarget(m_exe_path.c_str());
m_memory_change_create_target.Stop();
},
"time-create-target", "The time it takes to create a target."),
m_time_set_bp_main(
[this]() -> void {
m_memory_change_break_main.Start();
m_target.BreakpointCreateByName("main");
m_memory_change_break_main.Stop();
},
"time-set-break-main",
"Elapsed time it takes to set a breakpoint at 'main' by name."),
m_memory_change_create_target(), m_memory_change_break_main(),
m_memory_total(), m_time_launch_stop_main(), m_time_total(),
m_expr_first_evaluate(
[this](SBFrame frame) -> void {
frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()")
.GetError();
},
"time-expr", "Elapsed time it takes to evaluate an expression for "
"the first time."),
m_expr_frame_zero(
[this](SBFrame frame) -> void {
frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()")
.GetError();
},
"time-expr-frame-zero", "Elapsed time it takes to evaluate an "
"expression 3 times at frame zero."),
m_expr_frame_non_zero(
[this](SBFrame frame) -> void {
frame.EvaluateExpression("Diags.DiagArgumentsStr[0].size()")
.GetError();
},
"time-expr-frame-non-zero", "Elapsed time it takes to evaluate an "
"expression 3 times at a non-zero "
"frame."),
m_exe_path(), m_out_path(), m_launch_info(NULL), m_use_dsym(false) {}
virtual ~ClangTest() {}
virtual bool Setup(int &argc, const char **&argv) {
if (m_exe_path.empty())
return false;
m_launch_info.SetArguments(argv, false);
return true;
}
void DoTest() {}
virtual void TestStep(int counter, ActionWanted &next_action) {
char temp_source_path[PATH_MAX] = "/tmp/main.XXXXXX.cpp";
switch (counter) {
case 0: {
// Xcode::RunCommand(m_debugger,"log enable -f /tmp/packets.txt gdb-remote
// packets",true);
m_memory_total.Start();
m_time_total.Start();
// Time creating the target
m_time_create_target();
m_time_set_bp_main();
int fd = mkstemps(temp_source_path, 4);
if (fd >= 0) {
const char *source_content = R"(
#include <stdint.h>
#include <stdio.h>
#include <vector>
namespace {
struct Foo
{
int i; int j;
};
void doit (const Foo &foo)
{
printf ("doit(%i)\n", foo.i);
}
}
int main (int argc, char const *argv[], char const *envp[])
{
std::vector<int> ints;
for (int i=0;i<10;++i)
ints.push_back(i);
printf ("hello world\n");
Foo foo = { 12, 13 };
doit (foo);
return 0;
}
)";
write(fd, source_content, strlen(source_content));
close(fd);
} else {
const char *error_cstr = strerror(errno);
fprintf(stderr,
"error: failed to created temporary source file: '%s' (%s)",
temp_source_path, error_cstr);
exit(2);
}
m_time_launch_stop_main.Start();
const char *clang_argv[] = {"-cc1",
"-triple",
"x86_64-apple-macosx10.8.0",
"-emit-obj",
"-mrelax-all",
"-disable-free",
"-disable-llvm-verifier",
"-main-file-name",
"main.cpp",
"-mrelocation-model",
"pic",
"-pic-level",
"2",
"-mdisable-fp-elim",
"-masm-verbose",
"-munwind-tables",
"-target-cpu",
"core2",
"-target-linker-version",
"132.10.1",
"-v",
"-g",
"-O0",
"-fdeprecated-macro",
"-ferror-limit",
"19",
"-fmessage-length",
"298",
"-stack-protector",
"1",
"-mstackrealign",
"-fblocks",
"-fobjc-runtime=macosx-10.8.0",
"-fobjc-dispatch-method=mixed",
"-fencode-extended-block-signature",
"-fcxx-exceptions",
"-fexceptions",
"-fdiagnostics-show-option",
"-fcolor-diagnostics",
"-backend-option",
"-vectorize-loops",
"-o",
"/tmp/main.o",
"-x",
"c++",
NULL,
NULL};
clang_argv[llvm::array_lengthof(clang_argv) - 2] = temp_source_path;
SBLaunchInfo launch_info(clang_argv);
Launch(launch_info);
next_action
.None(); // Don't continue or do anything, just wait for next event...
} break;
case 1: {
m_time_launch_stop_main.Stop();
m_time_total.Stop();
SBFrame frame(m_thread.GetFrameAtIndex(0));
// Time the first expression evaluation
m_expr_first_evaluate(frame);
SBValue result;
for (size_t i = 0; i < NUM_EXPR_ITERATIONS; ++i) {
m_expr_frame_zero(frame);
}
m_target.BreakpointCreateByName("DeclContext::lookup");
next_action.Continue();
} break;
case 2: {
SBFrame frame(m_thread.GetFrameAtIndex(21));
SBValue result;
for (size_t i = 0; i < NUM_EXPR_ITERATIONS; ++i) {
m_expr_frame_non_zero(frame);
}
next_action.Continue();
} break;
default:
m_memory_total.Stop();
next_action.Kill();
break;
}
}
void WriteResults(Results &results) {
Results::Dictionary &results_dict = results.GetDictionary();
m_time_set_bp_main.WriteAverageAndStandardDeviation(results);
results_dict.Add(
"memory-change-create-target",
"Memory increase that occurs due to creating the target.",
m_memory_change_create_target.GetDeltaValue().GetResult(NULL, NULL));
results_dict.Add(
"memory-change-break-main", "Memory increase that occurs due to "
"setting a breakpoint at main by name.",
m_memory_change_break_main.GetDeltaValue().GetResult(NULL, NULL));
m_time_create_target.WriteAverageAndStandardDeviation(results);
m_expr_first_evaluate.WriteAverageAndStandardDeviation(results);
m_expr_frame_zero.WriteAverageAndStandardDeviation(results);
m_expr_frame_non_zero.WriteAverageAndStandardDeviation(results);
results_dict.Add("memory-total-break-main",
"The total memory that the current process is using after "
"setting the first breakpoint.",
m_memory_total.GetStopValue().GetResult(NULL, NULL));
results_dict.AddDouble(
"time-launch-stop-main",
"The time it takes to launch the process and stop at main.",
m_time_launch_stop_main.GetDeltaValue());
results_dict.AddDouble(
"time-total", "The time it takes to create the target, set breakpoint "
"at main, launch clang and hit the breakpoint at main.",
m_time_total.GetDeltaValue());
results.Write(GetResultFilePath());
}
const char *GetExecutablePath() const {
if (m_exe_path.empty())
return NULL;
return m_exe_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_exe_path = path;
else
m_exe_path.clear();
}
void SetResultFilePath(const char *path) {
if (path && path[0])
m_out_path = path;
else
m_out_path.clear();
}
void SetUseDSYM(bool b) { m_use_dsym = b; }
private:
// C++ formatters
TimeMeasurement<std::function<void()>> m_time_create_target;
TimeMeasurement<std::function<void()>> m_time_set_bp_main;
MemoryGauge m_memory_change_create_target;
MemoryGauge m_memory_change_break_main;
MemoryGauge m_memory_total;
TimeGauge m_time_launch_stop_main;
TimeGauge m_time_total;
TimeMeasurement<std::function<void(SBFrame)>> m_expr_first_evaluate;
TimeMeasurement<std::function<void(SBFrame)>> m_expr_frame_zero;
TimeMeasurement<std::function<void(SBFrame)>> m_expr_frame_non_zero;
std::string m_exe_path;
std::string m_out_path;
SBLaunchInfo m_launch_info;
bool m_use_dsym;
};
struct Options {
std::string clang_path;
std::string out_file;
bool verbose;
bool use_dsym;
bool error;
bool print_help;
Options() : verbose(false), error(false), print_help(false) {}
};
static struct option g_long_options[] = {
{"verbose", no_argument, NULL, 'v'},
{"clang", required_argument, NULL, 'c'},
{"out-file", required_argument, NULL, 'o'},
{"dsym", no_argument, NULL, 'd'},
{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));
ClangTest 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<char **>(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 'c': {
SBFileSpec file(optarg);
if (file.Exists())
test.SetExecutablePath(optarg);
else
fprintf(stderr, "error: file specified in --clang (-c) option doesn't "
"exist: '%s'\n",
optarg);
} break;
case 'o':
test.SetResultFilePath(optarg);
break;
case 'd':
test.SetUseDSYM(true);
break;
default:
option_data.error = true;
option_data.print_help = true;
fprintf(stderr, "error: unrecognized option %c\n", short_option);
break;
}
}
if (test.GetExecutablePath() == NULL) {
// --clang is mandatory
option_data.print_help = true;
option_data.error = true;
fprintf(stderr, "error: the '--clang=PATH' option is mandatory\n");
}
if (option_data.print_help) {
puts(R"(
NAME
lldb_perf_clang -- a tool that measures LLDB peformance while debugging clang.
SYNOPSIS
lldb_perf_clang --clang=PATH [--out-file=PATH --verbose --dsym] -- [clang options]
DESCRIPTION
Runs a set of static timing and memory tasks against clang and outputs results
to a plist file.
)");
}
if (option_data.error) {
exit(1);
}
// Update argc and argv after parsing options
argc -= optind;
argv += optind;
test.SetVerbose(true);
TestCase::Run(test, argc, argv);
return 0;
}

View File

@@ -0,0 +1,20 @@
#include <stdint.h>
#include <stdio.h>
#include <vector>
namespace {
struct Foo {
int i;
int j;
};
void doit(const Foo &foo) { printf("doit(%i)\n", foo.i); }
}
int main(int argc, char const *argv[], char const *envp[]) {
std::vector<int> ints;
for (int i = 0; i < 10; ++i)
ints.push_back(i);
printf("hello world\n");
Foo foo = {12, 13};
doit(foo);
return 0;
}

View File

@@ -0,0 +1,287 @@
#include <CoreFoundation/CoreFoundation.h>
#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 <getopt.h>
#include <string>
#include <unistd.h>
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<no_function> m_time_measurements;
std::vector<double> 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<char **>(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;
}

View File

@@ -0,0 +1,35 @@
#include <stdio.h>
#include <string>
#include <vector>
struct struct_for_copying {
struct_for_copying(int in_int, double in_double, const char *in_string)
: int_value(in_int), double_value(in_double), string_value(in_string) {}
struct_for_copying() { struct_for_copying(0, 0, ""); }
int int_value;
double double_value;
std::string string_value;
};
int main(int argc, char **argv) {
struct_for_copying input_struct(150 * argc, 10.0 * argc, argv[0]);
struct_for_copying output_struct;
int some_int = 44;
double some_double = 34.5;
double other_double;
size_t vector_size;
std::vector<struct_for_copying> my_vector;
printf("Here is some code to stop at originally. Got: %d, %p.\n", argc,
argv);
output_struct = input_struct;
other_double = (some_double * some_int) / ((double)argc);
other_double = other_double > 0
? some_double / other_double
: some_double > 0 ? other_double / some_double : 10.0;
my_vector.push_back(input_struct);
vector_size = my_vector.size();
return vector_size == 0 ? 0 : 1;
}

View File

@@ -0,0 +1,82 @@
//===-- fmts_tester.cpp -----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#import <Cocoa/Cocoa.h>
#include <list>
#include <map>
#include <string>
#include <vector>
int main() {
NSArray *nsarray = @[ @1, @2, @"hello world", @3, @4, @"foobar" ];
NSMutableArray *nsmutablearray = [[NSMutableArray alloc] initWithCapacity:5];
[nsmutablearray addObject:@1];
[nsmutablearray addObject:@2];
[nsmutablearray addObject:@"hello world"];
[nsmutablearray addObject:@3];
[nsmutablearray addObject:@4];
[nsmutablearray addObject:@"foobar"];
NSDictionary *nsdictionary =
@{ @1 : @1,
@2 : @2,
@"hello" : @"world",
@3 : @3 };
NSMutableDictionary *nsmutabledictionary =
[[NSMutableDictionary alloc] initWithCapacity:5];
[nsmutabledictionary setObject:@1 forKey:@1];
[nsmutabledictionary setObject:@2 forKey:@2];
[nsmutabledictionary setObject:@"hello" forKey:@"world"];
[nsmutabledictionary setObject:@3 forKey:@3];
NSString *str0 = @"Hello world";
NSString *str1 = @"Hello ℥";
NSString *str2 = @"Hello world";
NSString *str3 = @"Hello ℥";
NSString *str4 = @"Hello world";
NSDate *me = [NSDate dateWithNaturalLanguageString:@"April 10, 1985"];
NSDate *cutie = [NSDate dateWithNaturalLanguageString:@"January 29, 1983"];
NSDate *mom = [NSDate dateWithNaturalLanguageString:@"May 24, 1959"];
NSDate *dad = [NSDate dateWithNaturalLanguageString:@"October 29, 1954"];
NSDate *today = [NSDate dateWithNaturalLanguageString:@"March 14, 2013"];
NSArray *bundles = [NSBundle allBundles];
NSArray *frameworks = [NSBundle allFrameworks];
NSSet *nsset = [NSSet setWithArray:nsarray];
NSMutableSet *nsmutableset = [NSMutableSet setWithCapacity:5];
[nsmutableset addObject:@1];
[nsmutableset addObject:@2];
[nsmutableset addObject:@"hello world"];
[nsmutableset addObject:@3];
[nsmutableset addObject:@4];
[nsmutableset addObject:@"foobar"];
std::vector<int> vector;
vector.push_back(1);
vector.push_back(2);
vector.push_back(3);
vector.push_back(4);
vector.push_back(5);
std::list<int> list;
list.push_back(1);
list.push_back(2);
list.push_back(3);
list.push_back(4);
list.push_back(5);
std::map<int, int> map;
map[1] = 1;
map[2] = 2;
map[3] = 3;
map[4] = 4;
map[5] = 5;
std::string sstr0("Hello world");
std::string sstr1("Hello world");
std::string sstr2("Hello world");
std::string sstr3("Hello world");
std::string sstr4("Hello world");
int x = 0;
for (;;)
x++;
}

View File

@@ -0,0 +1,268 @@
//===-- formatters.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/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 <fstream>
#include <iostream>
#include <unistd.h>
using namespace lldb_perf;
class FormattersTest : public TestCase {
public:
FormattersTest() : TestCase() {
m_dump_std_vector_measurement = CreateTimeMeasurement(
[](SBValue value) -> void {
lldb_perf::Xcode::FetchVariable(value, 1, false);
},
"std-vector", "time to dump an std::vector");
m_dump_std_list_measurement = CreateTimeMeasurement(
[](SBValue value) -> void {
lldb_perf::Xcode::FetchVariable(value, 1, false);
},
"std-list", "time to dump an std::list");
m_dump_std_map_measurement = CreateTimeMeasurement(
[](SBValue value) -> void {
lldb_perf::Xcode::FetchVariable(value, 1, false);
},
"std-map", "time to dump an std::map");
// use this in manual mode
m_dump_std_string_measurement = CreateTimeMeasurement(
[]() -> void {}, "std-string", "time to dump an std::string");
m_dump_nsstring_measurement = CreateTimeMeasurement(
[](SBValue value) -> void {
lldb_perf::Xcode::FetchVariable(value, 0, false);
},
"ns-string", "time to dump an NSString");
m_dump_nsarray_measurement = CreateTimeMeasurement(
[](SBValue value) -> void {
lldb_perf::Xcode::FetchVariable(value, 1, false);
},
"ns-array", "time to dump an NSArray");
m_dump_nsdictionary_measurement = CreateTimeMeasurement(
[](SBValue value) -> void {
lldb_perf::Xcode::FetchVariable(value, 1, false);
},
"ns-dictionary", "time to dump an NSDictionary");
m_dump_nsset_measurement = CreateTimeMeasurement(
[](SBValue value) -> void {
lldb_perf::Xcode::FetchVariable(value, 1, false);
},
"ns-set", "time to dump an NSSet");
m_dump_nsbundle_measurement = CreateTimeMeasurement(
[](SBValue value) -> void {
lldb_perf::Xcode::FetchVariable(value, 1, false);
},
"ns-bundle", "time to dump an NSBundle");
m_dump_nsdate_measurement = CreateTimeMeasurement(
[](SBValue value) -> void {
lldb_perf::Xcode::FetchVariable(value, 0, false);
},
"ns-date", "time to dump an NSDate");
}
virtual ~FormattersTest() {}
virtual bool Setup(int &argc, const char **&argv) {
m_app_path.assign(argv[1]);
m_out_path.assign(argv[2]);
m_target = m_debugger.CreateTarget(m_app_path.c_str());
m_target.BreakpointCreateByName("main");
SBLaunchInfo launch_info(argv);
return Launch(launch_info);
}
void DoTest() {
SBFrame frame_zero(m_thread.GetFrameAtIndex(0));
m_dump_nsarray_measurement(
frame_zero.FindVariable("nsarray", lldb::eDynamicCanRunTarget));
m_dump_nsarray_measurement(
frame_zero.FindVariable("nsmutablearray", lldb::eDynamicCanRunTarget));
m_dump_nsdictionary_measurement(
frame_zero.FindVariable("nsdictionary", lldb::eDynamicCanRunTarget));
m_dump_nsdictionary_measurement(frame_zero.FindVariable(
"nsmutabledictionary", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(
frame_zero.FindVariable("str0", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(
frame_zero.FindVariable("str1", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(
frame_zero.FindVariable("str2", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(
frame_zero.FindVariable("str3", lldb::eDynamicCanRunTarget));
m_dump_nsstring_measurement(
frame_zero.FindVariable("str4", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(
frame_zero.FindVariable("me", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(
frame_zero.FindVariable("cutie", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(
frame_zero.FindVariable("mom", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(
frame_zero.FindVariable("dad", lldb::eDynamicCanRunTarget));
m_dump_nsdate_measurement(
frame_zero.FindVariable("today", lldb::eDynamicCanRunTarget));
m_dump_nsbundle_measurement(
frame_zero.FindVariable("bundles", lldb::eDynamicCanRunTarget));
m_dump_nsbundle_measurement(
frame_zero.FindVariable("frameworks", lldb::eDynamicCanRunTarget));
m_dump_nsset_measurement(
frame_zero.FindVariable("nsset", lldb::eDynamicCanRunTarget));
m_dump_nsset_measurement(
frame_zero.FindVariable("nsmutableset", lldb::eDynamicCanRunTarget));
m_dump_std_vector_measurement(
frame_zero.FindVariable("vector", lldb::eDynamicCanRunTarget));
m_dump_std_list_measurement(
frame_zero.FindVariable("list", lldb::eDynamicCanRunTarget));
m_dump_std_map_measurement(
frame_zero.FindVariable("map", lldb::eDynamicCanRunTarget));
auto sstr0 = frame_zero.FindVariable("sstr0", lldb::eDynamicCanRunTarget);
auto sstr1 = frame_zero.FindVariable("sstr1", lldb::eDynamicCanRunTarget);
auto sstr2 = frame_zero.FindVariable("sstr2", lldb::eDynamicCanRunTarget);
auto sstr3 = frame_zero.FindVariable("sstr3", lldb::eDynamicCanRunTarget);
auto sstr4 = frame_zero.FindVariable("sstr4", lldb::eDynamicCanRunTarget);
m_dump_std_string_measurement.Start();
Xcode::FetchVariable(sstr0, 0, false);
m_dump_std_string_measurement.Stop();
m_dump_std_string_measurement.Start();
Xcode::FetchVariable(sstr1, 0, false);
m_dump_std_string_measurement.Stop();
m_dump_std_string_measurement.Start();
Xcode::FetchVariable(sstr2, 0, false);
m_dump_std_string_measurement.Stop();
m_dump_std_string_measurement.Start();
Xcode::FetchVariable(sstr3, 0, false);
m_dump_std_string_measurement.Stop();
m_dump_std_string_measurement.Start();
Xcode::FetchVariable(sstr4, 0, false);
m_dump_std_string_measurement.Stop();
}
virtual void TestStep(int counter, ActionWanted &next_action) {
switch (counter) {
case 0:
m_target.BreakpointCreateByLocation("fmts_tester.mm", 78);
next_action.Continue();
break;
case 1:
DoTest();
next_action.Continue();
break;
case 2:
DoTest();
next_action.Continue();
break;
case 3:
DoTest();
next_action.Continue();
break;
case 4:
DoTest();
next_action.Continue();
break;
case 5:
DoTest();
next_action.Continue();
break;
case 6:
DoTest();
next_action.Continue();
break;
case 7:
DoTest();
next_action.Continue();
break;
case 8:
DoTest();
next_action.Continue();
break;
case 9:
DoTest();
next_action.Continue();
break;
case 10:
DoTest();
next_action.Continue();
break;
default:
next_action.Kill();
break;
}
}
virtual void WriteResults(Results &results) {
m_dump_std_vector_measurement.WriteAverageAndStandardDeviation(results);
m_dump_std_list_measurement.WriteAverageAndStandardDeviation(results);
m_dump_std_map_measurement.WriteAverageAndStandardDeviation(results);
m_dump_std_string_measurement.WriteAverageAndStandardDeviation(results);
m_dump_nsstring_measurement.WriteAverageAndStandardDeviation(results);
m_dump_nsarray_measurement.WriteAverageAndStandardDeviation(results);
m_dump_nsdictionary_measurement.WriteAverageAndStandardDeviation(results);
m_dump_nsset_measurement.WriteAverageAndStandardDeviation(results);
m_dump_nsbundle_measurement.WriteAverageAndStandardDeviation(results);
m_dump_nsdate_measurement.WriteAverageAndStandardDeviation(results);
results.Write(m_out_path.c_str());
}
private:
// C++ formatters
TimeMeasurement<std::function<void(SBValue)>> m_dump_std_vector_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_std_list_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_std_map_measurement;
TimeMeasurement<std::function<void()>> m_dump_std_string_measurement;
// Cocoa formatters
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsstring_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsarray_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsdictionary_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsset_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsbundle_measurement;
TimeMeasurement<std::function<void(SBValue)>> m_dump_nsdate_measurement;
// useful files
std::string m_app_path;
std::string m_out_path;
};
// argv[1] == path to app
// argv[2] == path to result
int main(int argc, const char *argv[]) {
FormattersTest frmtest;
frmtest.SetVerbose(true);
TestCase::Run(frmtest, argc, argv);
return 0;
}

View File

@@ -0,0 +1,330 @@
//===-- 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/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 <fstream>
#include <getopt.h>
#include <iostream>
#include <unistd.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);
}

View File

@@ -0,0 +1,50 @@
//===-- Gauge.cpp -----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Gauge.h"
#include "lldb/lldb-forward.h"
template <>
lldb_perf::Results::ResultSP lldb_perf::GetResult(const char *description,
double value) {
if (description && description[0]) {
std::unique_ptr<Results::Dictionary> value_dict_ap(
new Results::Dictionary());
value_dict_ap->AddString("description", NULL, description);
value_dict_ap->AddDouble("value", NULL, value);
return Results::ResultSP(value_dict_ap.release());
}
return Results::ResultSP(new Results::Double(NULL, NULL, value));
}
template <>
lldb_perf::Results::ResultSP lldb_perf::GetResult(const char *description,
uint64_t value) {
if (description && description[0]) {
std::unique_ptr<Results::Dictionary> value_dict_ap(
new Results::Dictionary());
value_dict_ap->AddString("description", NULL, description);
value_dict_ap->AddUnsigned("value", NULL, value);
return Results::ResultSP(value_dict_ap.release());
}
return Results::ResultSP(new Results::Unsigned(NULL, NULL, value));
}
template <>
lldb_perf::Results::ResultSP lldb_perf::GetResult(const char *description,
std::string value) {
if (description && description[0]) {
std::unique_ptr<Results::Dictionary> value_dict_ap(
new Results::Dictionary());
value_dict_ap->AddString("description", NULL, description);
value_dict_ap->AddString("value", NULL, value.c_str());
return Results::ResultSP(value_dict_ap.release());
}
return Results::ResultSP(new Results::String(NULL, NULL, value.c_str()));
}

View File

@@ -0,0 +1,51 @@
//===-- Gauge.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef PerfTestDriver_Gauge_h
#define PerfTestDriver_Gauge_h
#include <functional>
#include <string>
#include "Results.h"
namespace lldb_perf {
template <class T> class Gauge {
public:
typedef T ValueType;
Gauge() {}
virtual ~Gauge() {}
virtual void Start() = 0;
virtual ValueType Stop() = 0;
virtual ValueType GetStartValue() const = 0;
virtual ValueType GetStopValue() const = 0;
virtual ValueType GetDeltaValue() const = 0;
};
template <class T>
Results::ResultSP GetResult(const char *description, T value);
template <> Results::ResultSP GetResult(const char *description, double value);
template <>
Results::ResultSP GetResult(const char *description, uint64_t value);
template <>
Results::ResultSP GetResult(const char *description, std::string value);
}
#endif

View File

@@ -0,0 +1,158 @@
//===-- Measurement.h -------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef __PerfTestDriver__Measurement__
#define __PerfTestDriver__Measurement__
#include "Gauge.h"
#include "MemoryGauge.h"
#include "Metric.h"
#include "Timer.h"
namespace lldb_perf {
template <typename GaugeType, typename Callable> class Measurement {
public:
Measurement() : m_gauge(), m_callable(), m_metric() {}
Measurement(Callable callable, const char *name, const char *desc)
: m_gauge(), m_callable(callable),
m_metric(Metric<typename GaugeType::ValueType>(name, desc)) {}
Measurement(const char *name, const char *desc)
: m_gauge(), m_callable(),
m_metric(Metric<typename GaugeType::ValueType>(name, desc)) {}
template <typename GaugeType_Rhs, typename Callable_Rhs>
Measurement(const Measurement<GaugeType_Rhs, Callable_Rhs> &rhs)
: m_gauge(rhs.GetGauge()), m_callable(rhs.GetCallable()),
m_metric(rhs.GetMetric()) {}
template <typename... Args> void operator()(Args... args) {
m_gauge.Start();
m_callable(args...);
m_metric.Append(m_gauge.Stop());
}
virtual const Callable &GetCallable() const { return m_callable; }
virtual const GaugeType &GetGauge() const { return m_gauge; }
virtual const Metric<typename GaugeType::ValueType> &GetMetric() const {
return m_metric;
}
void Start() { m_gauge.Start(); }
typename GaugeType::ValueType Stop() {
auto value = m_gauge.Stop();
m_metric.Append(value);
return value;
}
void WriteStartValue(Results &results) {
auto metric = GetMetric();
results.GetDictionary().Add(
metric.GetName(), metric.GetDescription(),
lldb_perf::GetResult<typename GaugeType::ValueType>(
NULL, metric.GetStartValue()));
}
void WriteStopValue(Results &results) {
auto metric = GetMetric();
results.GetDictionary().Add(
metric.GetName(), metric.GetDescription(),
lldb_perf::GetResult<typename GaugeType::ValueType>(
NULL, metric.GetStopValue()));
}
void WriteAverageValue(Results &results) {
auto metric = GetMetric();
results.GetDictionary().Add(
metric.GetName(), metric.GetDescription(),
lldb_perf::GetResult<typename GaugeType::ValueType>(
NULL, metric.GetAverage()));
}
void WriteAverageAndStandardDeviation(Results &results) {
auto metric = GetMetric();
auto dictionary =
(Results::Dictionary *)results.GetDictionary()
.Add(metric.GetName(), metric.GetDescription(),
lldb_perf::GetResult<typename GaugeType::ValueType>(
NULL, metric.GetAverage()))
.get();
if (dictionary) {
dictionary->Add("stddev", NULL,
lldb_perf::GetResult<typename GaugeType::ValueType>(
NULL, metric.GetStandardDeviation()));
}
}
void WriteStandardDeviation(Results &results) {
auto metric = GetMetric();
results.GetDictionary().Add(
metric.GetName(), metric.GetDescription(),
lldb_perf::GetResult<typename GaugeType::ValueType>(
NULL, metric.GetStandardDeviation()));
}
protected:
GaugeType m_gauge;
Callable m_callable;
Metric<typename GaugeType::ValueType> m_metric;
};
template <typename Callable>
class TimeMeasurement : public Measurement<TimeGauge, Callable> {
public:
TimeMeasurement() : Measurement<TimeGauge, Callable>() {}
TimeMeasurement(Callable callable, const char *name = NULL,
const char *descr = NULL)
: Measurement<TimeGauge, Callable>(callable, name, descr) {}
template <typename Callable_Rhs>
TimeMeasurement(const TimeMeasurement<Callable_Rhs> &rhs)
: Measurement<TimeGauge, Callable>(rhs) {}
template <typename GaugeType_Rhs, typename Callable_Rhs>
TimeMeasurement(const Measurement<GaugeType_Rhs, Callable_Rhs> &rhs)
: Measurement<GaugeType_Rhs, Callable_Rhs>(rhs) {}
template <typename... Args> void operator()(Args... args) {
Measurement<TimeGauge, Callable>::operator()(args...);
}
};
template <typename Callable>
class MemoryMeasurement : public Measurement<MemoryGauge, Callable> {
public:
MemoryMeasurement() : Measurement<MemoryGauge, Callable>() {}
MemoryMeasurement(Callable callable, const char *name, const char *descr)
: Measurement<MemoryGauge, Callable>(callable, name, descr) {}
MemoryMeasurement(const char *name, const char *descr)
: Measurement<MemoryGauge, Callable>(name, descr) {}
template <typename Callable_Rhs>
MemoryMeasurement(const MemoryMeasurement<Callable_Rhs> &rhs)
: Measurement<MemoryGauge, Callable>(rhs) {}
template <typename GaugeType_Rhs, typename Callable_Rhs>
MemoryMeasurement(const Measurement<GaugeType_Rhs, Callable_Rhs> &rhs)
: Measurement<GaugeType_Rhs, Callable_Rhs>(rhs) {}
template <typename... Args> void operator()(Args... args) {
Measurement<MemoryGauge, Callable>::operator()(args...);
}
};
}
#endif /* defined(__PerfTestDriver__Measurement__) */

View File

@@ -0,0 +1,133 @@
//===-- MemoryGauge.cpp -----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "MemoryGauge.h"
#include "lldb/lldb-forward.h"
#include <assert.h>
#include <cmath>
#include <mach/mach.h>
#include <mach/mach_traps.h>
#include <mach/task.h>
using namespace lldb_perf;
MemoryStats::MemoryStats(mach_vm_size_t virtual_size,
mach_vm_size_t resident_size,
mach_vm_size_t max_resident_size)
: m_virtual_size(virtual_size), m_resident_size(resident_size),
m_max_resident_size(max_resident_size) {}
MemoryStats::MemoryStats(const MemoryStats &rhs)
: m_virtual_size(rhs.m_virtual_size), m_resident_size(rhs.m_resident_size),
m_max_resident_size(rhs.m_max_resident_size) {}
MemoryStats &MemoryStats::operator=(const MemoryStats &rhs) {
if (this != &rhs) {
m_virtual_size = rhs.m_virtual_size;
m_resident_size = rhs.m_resident_size;
m_max_resident_size = rhs.m_max_resident_size;
}
return *this;
}
MemoryStats &MemoryStats::operator+=(const MemoryStats &rhs) {
m_virtual_size += rhs.m_virtual_size;
m_resident_size += rhs.m_resident_size;
m_max_resident_size += rhs.m_max_resident_size;
return *this;
}
MemoryStats MemoryStats::operator-(const MemoryStats &rhs) {
return MemoryStats(m_virtual_size - rhs.m_virtual_size,
m_resident_size - rhs.m_resident_size,
m_max_resident_size - rhs.m_max_resident_size);
}
MemoryStats MemoryStats::operator+(const MemoryStats &rhs) {
return MemoryStats(m_virtual_size + rhs.m_virtual_size,
m_resident_size + rhs.m_resident_size,
m_max_resident_size + rhs.m_max_resident_size);
}
MemoryStats MemoryStats::operator/(size_t n) {
MemoryStats result(*this);
result.m_virtual_size /= n;
result.m_resident_size /= n;
result.m_max_resident_size /= n;
return result;
}
MemoryStats MemoryStats::operator*(const MemoryStats &rhs) {
return MemoryStats(m_virtual_size * rhs.m_virtual_size,
m_resident_size * rhs.m_resident_size,
m_max_resident_size * rhs.m_max_resident_size);
}
Results::ResultSP MemoryStats::GetResult(const char *name,
const char *description) const {
std::unique_ptr<Results::Dictionary> dict_ap(
new Results::Dictionary(name, NULL));
dict_ap->AddUnsigned("resident", NULL, GetResidentSize());
dict_ap->AddUnsigned("max_resident", NULL, GetMaxResidentSize());
return Results::ResultSP(dict_ap.release());
}
MemoryGauge::ValueType MemoryGauge::Now() {
task_t task = mach_task_self();
mach_task_basic_info_data_t taskBasicInfo;
mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
auto task_info_ret = task_info(task, MACH_TASK_BASIC_INFO,
(task_info_t)&taskBasicInfo, &count);
if (task_info_ret == KERN_SUCCESS) {
return MemoryStats(taskBasicInfo.virtual_size, taskBasicInfo.resident_size,
taskBasicInfo.resident_size_max);
}
return 0;
}
MemoryGauge::MemoryGauge()
: m_state(MemoryGauge::State::eNeverUsed), m_start(), m_delta() {}
void MemoryGauge::Start() {
m_state = MemoryGauge::State::eCounting;
m_start = Now();
}
MemoryGauge::ValueType MemoryGauge::Stop() {
m_stop = Now();
assert(m_state == MemoryGauge::State::eCounting &&
"cannot stop a non-started gauge");
m_state = MemoryGauge::State::eStopped;
m_delta = m_stop - m_start;
return m_delta;
}
MemoryGauge::ValueType MemoryGauge::GetDeltaValue() const {
assert(m_state == MemoryGauge::State::eStopped &&
"gauge must be used before you can evaluate it");
return m_delta;
}
template <>
Results::ResultSP lldb_perf::GetResult(const char *description,
MemoryStats value) {
return value.GetResult(NULL, description);
}
MemoryStats sqrt(const MemoryStats &arg) {
long double virt_size = arg.GetVirtualSize();
long double resident_size = arg.GetResidentSize();
long double max_resident_size = arg.GetMaxResidentSize();
virt_size = sqrtl(virt_size);
resident_size = sqrtl(resident_size);
max_resident_size = sqrtl(max_resident_size);
return MemoryStats(virt_size, resident_size, max_resident_size);
}

View File

@@ -0,0 +1,92 @@
//===-- MemoryGauge.h -------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef __PerfTestDriver__MemoryGauge__
#define __PerfTestDriver__MemoryGauge__
#include "Gauge.h"
#include "Results.h"
#include <mach/task_info.h>
namespace lldb_perf {
class MemoryStats {
public:
MemoryStats(mach_vm_size_t virtual_size = 0, mach_vm_size_t resident_size = 0,
mach_vm_size_t max_resident_size = 0);
MemoryStats(const MemoryStats &rhs);
MemoryStats &operator=(const MemoryStats &rhs);
MemoryStats &operator+=(const MemoryStats &rhs);
MemoryStats operator-(const MemoryStats &rhs);
MemoryStats operator+(const MemoryStats &rhs);
MemoryStats operator/(size_t rhs);
MemoryStats operator*(const MemoryStats &rhs);
mach_vm_size_t GetVirtualSize() const { return m_virtual_size; }
mach_vm_size_t GetResidentSize() const { return m_resident_size; }
mach_vm_size_t GetMaxResidentSize() const { return m_max_resident_size; }
void SetVirtualSize(mach_vm_size_t vs) { m_virtual_size = vs; }
void SetResidentSize(mach_vm_size_t rs) { m_resident_size = rs; }
void SetMaxResidentSize(mach_vm_size_t mrs) { m_max_resident_size = mrs; }
Results::ResultSP GetResult(const char *name, const char *description) const;
private:
mach_vm_size_t m_virtual_size;
mach_vm_size_t m_resident_size;
mach_vm_size_t m_max_resident_size;
};
class MemoryGauge : public Gauge<MemoryStats> {
public:
MemoryGauge();
virtual ~MemoryGauge() {}
void Start();
ValueType Stop();
virtual ValueType GetStartValue() const { return m_start; }
virtual ValueType GetStopValue() const { return m_stop; }
virtual ValueType GetDeltaValue() const;
private:
enum class State { eNeverUsed, eCounting, eStopped };
ValueType Now();
State m_state;
ValueType m_start;
ValueType m_stop;
ValueType m_delta;
};
template <>
Results::ResultSP GetResult(const char *description, MemoryStats value);
} // namespace lldb_perf
lldb_perf::MemoryStats sqrt(const lldb_perf::MemoryStats &arg);
#endif // #ifndef __PerfTestDriver__MemoryGauge__

View File

@@ -0,0 +1,60 @@
//===-- Metric.cpp ----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Metric.h"
#include "MemoryGauge.h"
#include <cmath>
using namespace lldb_perf;
template <class T> Metric<T>::Metric() : Metric("") {}
template <class T>
Metric<T>::Metric(const char *n, const char *d)
: m_name(n ? n : ""), m_description(d ? d : ""), m_dataset() {}
template <class T> void Metric<T>::Append(T v) { m_dataset.push_back(v); }
template <class T> size_t Metric<T>::GetCount() const {
return m_dataset.size();
}
template <class T> T Metric<T>::GetSum() const {
T sum = 0;
for (auto v : m_dataset)
sum += v;
return sum;
}
template <class T> T Metric<T>::GetAverage() const {
return GetSum() / GetCount();
}
// Knuth's algorithm for stddev - massive cancellation resistant
template <class T>
T Metric<T>::GetStandardDeviation(StandardDeviationMode mode) const {
size_t n = 0;
T mean = 0;
T M2 = 0;
for (auto x : m_dataset) {
n = n + 1;
T delta = x - mean;
mean = mean + delta / n;
M2 = M2 + delta * (x - mean);
}
T variance;
if (mode == StandardDeviationMode::ePopulation || n == 1)
variance = M2 / n;
else
variance = M2 / (n - 1);
return sqrt(variance);
}
template class lldb_perf::Metric<double>;
template class lldb_perf::Metric<MemoryStats>;

View File

@@ -0,0 +1,58 @@
//===-- Metric.h ------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef __PerfTestDriver__Metric__
#define __PerfTestDriver__Metric__
#include <mach/task_info.h>
#include <string>
#include <vector>
namespace lldb_perf {
class MemoryStats;
template <class ValueType> class Metric {
public:
enum class StandardDeviationMode { eSample, ePopulation };
Metric();
Metric(const char *, const char * = NULL);
void Append(ValueType v);
ValueType GetAverage() const;
size_t GetCount() const;
ValueType GetSum() const;
ValueType GetStandardDeviation(
StandardDeviationMode mode = StandardDeviationMode::ePopulation) const;
const char *GetName() const {
if (m_name.empty())
return NULL;
return m_name.c_str();
}
const char *GetDescription() const {
if (m_description.empty())
return NULL;
return m_description.c_str();
}
private:
std::string m_name;
std::string m_description;
std::vector<ValueType> m_dataset;
};
}
#endif /* defined(__PerfTestDriver__Metric__) */

View File

@@ -0,0 +1,237 @@
//===-- Results.cpp ---------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Results.h"
#include <assert.h>
#ifdef __APPLE__
#include "CFCMutableArray.h"
#include "CFCMutableDictionary.h"
#include "CFCReleaser.h"
#include "CFCString.h"
#endif
using namespace lldb_perf;
static void AddResultToArray(CFCMutableArray &array, Results::Result *result);
static void AddResultToDictionary(CFCMutableDictionary &parent_dict,
const char *key, Results::Result *result);
static void AddResultToArray(CFCMutableArray &parent_array,
Results::Result *result) {
switch (result->GetType()) {
case Results::Result::Type::Invalid:
break;
case Results::Result::Type::Array: {
Results::Array *value = result->GetAsArray();
CFCMutableArray array;
value->ForEach([&array](const Results::ResultSP &value_sp) -> bool {
AddResultToArray(array, value_sp.get());
return true;
});
parent_array.AppendValue(array.get(), true);
} break;
case Results::Result::Type::Dictionary: {
Results::Dictionary *value = result->GetAsDictionary();
CFCMutableDictionary dict;
value->ForEach([&dict](const std::string &key,
const Results::ResultSP &value_sp) -> bool {
AddResultToDictionary(dict, key.c_str(), value_sp.get());
return true;
});
if (result->GetDescription()) {
dict.AddValueCString(CFSTR("description"), result->GetDescription());
}
parent_array.AppendValue(dict.get(), true);
} break;
case Results::Result::Type::Double: {
double d = result->GetAsDouble()->GetValue();
CFCReleaser<CFNumberRef> cf_number(
::CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &d));
if (cf_number.get())
parent_array.AppendValue(cf_number.get(), true);
} break;
case Results::Result::Type::String: {
CFCString cfstr(result->GetAsString()->GetValue());
if (cfstr.get())
parent_array.AppendValue(cfstr.get(), true);
} break;
case Results::Result::Type::Unsigned: {
uint64_t uval64 = result->GetAsUnsigned()->GetValue();
CFCReleaser<CFNumberRef> cf_number(
::CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &uval64));
if (cf_number.get())
parent_array.AppendValue(cf_number.get(), true);
} break;
default:
llvm_unreachable("unhandled result");
}
}
static void AddResultToDictionary(CFCMutableDictionary &parent_dict,
const char *key, Results::Result *result) {
assert(key && key[0]);
CFCString cf_key(key);
switch (result->GetType()) {
case Results::Result::Type::Invalid:
break;
case Results::Result::Type::Array: {
Results::Array *value = result->GetAsArray();
CFCMutableArray array;
value->ForEach([&array](const Results::ResultSP &value_sp) -> bool {
AddResultToArray(array, value_sp.get());
return true;
});
parent_dict.AddValue(cf_key.get(), array.get(), true);
} break;
case Results::Result::Type::Dictionary: {
Results::Dictionary *value = result->GetAsDictionary();
CFCMutableDictionary dict;
value->ForEach([&dict](const std::string &key,
const Results::ResultSP &value_sp) -> bool {
AddResultToDictionary(dict, key.c_str(), value_sp.get());
return true;
});
if (result->GetDescription()) {
dict.AddValueCString(CFSTR("description"), result->GetDescription());
}
parent_dict.AddValue(cf_key.get(), dict.get(), true);
} break;
case Results::Result::Type::Double: {
parent_dict.SetValueDouble(cf_key.get(), result->GetAsDouble()->GetValue(),
true);
} break;
case Results::Result::Type::String: {
parent_dict.SetValueCString(cf_key.get(), result->GetAsString()->GetValue(),
true);
} break;
case Results::Result::Type::Unsigned: {
parent_dict.SetValueUInt64(cf_key.get(),
result->GetAsUnsigned()->GetValue(), true);
} break;
default:
llvm_unreachable("unhandled result");
}
}
void Results::Write(const char *out_path) {
#ifdef __APPLE__
CFCMutableDictionary dict;
m_results.ForEach(
[&dict](const std::string &key, const ResultSP &value_sp) -> bool {
AddResultToDictionary(dict, key.c_str(), value_sp.get());
return true;
});
CFDataRef xmlData = CFPropertyListCreateData(
kCFAllocatorDefault, dict.get(), kCFPropertyListXMLFormat_v1_0, 0, NULL);
if (out_path == NULL)
out_path = "/dev/stdout";
CFURLRef file = CFURLCreateFromFileSystemRepresentation(
NULL, (const UInt8 *)out_path, strlen(out_path), FALSE);
CFURLWriteDataAndPropertiesToResource(file, xmlData, NULL, NULL);
#endif
}
Results::ResultSP Results::Dictionary::AddUnsigned(const char *name,
const char *description,
uint64_t value) {
assert(name && name[0]);
if (description && description[0]) {
std::unique_ptr<Results::Dictionary> value_dict_ap(
new Results::Dictionary());
value_dict_ap->AddString("description", NULL, description);
value_dict_ap->AddUnsigned("value", NULL, value);
m_dictionary[std::string(name)] = ResultSP(value_dict_ap.release());
} else
m_dictionary[std::string(name)] =
ResultSP(new Unsigned(name, description, value));
return m_dictionary[std::string(name)];
}
Results::ResultSP Results::Dictionary::AddDouble(const char *name,
const char *description,
double value) {
assert(name && name[0]);
if (description && description[0]) {
std::unique_ptr<Results::Dictionary> value_dict_ap(
new Results::Dictionary());
value_dict_ap->AddString("description", NULL, description);
value_dict_ap->AddDouble("value", NULL, value);
m_dictionary[std::string(name)] = ResultSP(value_dict_ap.release());
} else
m_dictionary[std::string(name)] =
ResultSP(new Double(name, description, value));
return m_dictionary[std::string(name)];
}
Results::ResultSP Results::Dictionary::AddString(const char *name,
const char *description,
const char *value) {
assert(name && name[0]);
if (description && description[0]) {
std::unique_ptr<Results::Dictionary> value_dict_ap(
new Results::Dictionary());
value_dict_ap->AddString("description", NULL, description);
value_dict_ap->AddString("value", NULL, value);
m_dictionary[std::string(name)] = ResultSP(value_dict_ap.release());
} else
m_dictionary[std::string(name)] =
ResultSP(new String(name, description, value));
return m_dictionary[std::string(name)];
}
Results::ResultSP Results::Dictionary::Add(const char *name,
const char *description,
const ResultSP &result_sp) {
assert(name && name[0]);
if (description && description[0]) {
std::unique_ptr<Results::Dictionary> value_dict_ap(
new Results::Dictionary());
value_dict_ap->AddString("description", NULL, description);
value_dict_ap->Add("value", NULL, result_sp);
m_dictionary[std::string(name)] = ResultSP(value_dict_ap.release());
} else
m_dictionary[std::string(name)] = result_sp;
return m_dictionary[std::string(name)];
}
void Results::Dictionary::ForEach(
const std::function<bool(const std::string &, const ResultSP &)>
&callback) {
collection::const_iterator pos, end = m_dictionary.end();
for (pos = m_dictionary.begin(); pos != end; ++pos) {
if (callback(pos->first.c_str(), pos->second) == false)
return;
}
}
Results::ResultSP Results::Array::Append(const ResultSP &result_sp) {
m_array.push_back(result_sp);
return result_sp;
}
void Results::Array::ForEach(
const std::function<bool(const ResultSP &)> &callback) {
collection::const_iterator pos, end = m_array.end();
for (pos = m_array.begin(); pos != end; ++pos) {
if (callback(*pos) == false)
return;
}
}

View File

@@ -0,0 +1,205 @@
//===-- Results.h -----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef __PerfTestDriver_Results_h__
#define __PerfTestDriver_Results_h__
#include "lldb/lldb-forward.h"
#include <map>
#include <string>
#include <vector>
namespace lldb_perf {
class Results {
public:
class Array;
class Dictionary;
class Double;
class String;
class Unsigned;
class Result {
public:
enum class Type { Invalid, Array, Dictionary, Double, String, Unsigned };
Result(Type type, const char *name, const char *description)
: m_name(), m_description(), m_type(type) {
if (name && name[0])
m_name = name;
if (description && description[0])
m_description = description;
}
virtual ~Result() {}
virtual void Write(Results &results) = 0;
Array *GetAsArray() {
if (m_type == Type::Array)
return (Array *)this;
return NULL;
}
Dictionary *GetAsDictionary() {
if (m_type == Type::Dictionary)
return (Dictionary *)this;
return NULL;
}
Double *GetAsDouble() {
if (m_type == Type::Double)
return (Double *)this;
return NULL;
}
String *GetAsString() {
if (m_type == Type::String)
return (String *)this;
return NULL;
}
Unsigned *GetAsUnsigned() {
if (m_type == Type::Unsigned)
return (Unsigned *)this;
return NULL;
}
const char *GetName() const {
if (m_name.empty())
return NULL;
return m_name.c_str();
}
const char *GetDescription() const {
if (m_description.empty())
return NULL;
return m_description.c_str();
}
Type GetType() const { return m_type; }
protected:
std::string m_name;
std::string m_description;
Type m_type;
};
typedef std::shared_ptr<Result> ResultSP;
class Array : public Result {
public:
Array(const char *name, const char *description)
: Result(Type::Array, name, description) {}
virtual ~Array() {}
ResultSP Append(const ResultSP &result_sp);
void ForEach(const std::function<bool(const ResultSP &)> &callback);
virtual void Write(Results &results) {}
protected:
typedef std::vector<ResultSP> collection;
collection m_array;
};
class Dictionary : public Result {
public:
Dictionary() : Result(Type::Dictionary, NULL, NULL) {}
Dictionary(const char *name, const char *description)
: Result(Type::Dictionary, name, description) {}
virtual ~Dictionary() {}
virtual void Write(Results &results) {}
void ForEach(const std::function<bool(const std::string &,
const ResultSP &)> &callback);
ResultSP Add(const char *name, const char *description,
const ResultSP &result_sp);
ResultSP AddDouble(const char *name, const char *descriptiorn,
double value);
ResultSP AddUnsigned(const char *name, const char *description,
uint64_t value);
ResultSP AddString(const char *name, const char *description,
const char *value);
protected:
typedef std::map<std::string, ResultSP> collection;
collection m_dictionary;
};
class String : public Result {
public:
String(const char *name, const char *description, const char *value)
: Result(Type::String, name, description), m_string() {
if (value && value[0])
m_string = value;
}
virtual ~String() {}
virtual void Write(Results &results) {}
const char *GetValue() const {
return m_string.empty() ? NULL : m_string.c_str();
}
protected:
std::string m_string;
};
class Double : public Result {
public:
Double(const char *name, const char *description, double value)
: Result(Type::Double, name, description), m_double(value) {}
virtual ~Double() {}
virtual void Write(Results &results) {}
double GetValue() const { return m_double; }
protected:
double m_double;
};
class Unsigned : public Result {
public:
Unsigned(const char *name, const char *description, uint64_t value)
: Result(Type::Unsigned, name, description), m_unsigned(value) {}
virtual ~Unsigned() {}
virtual void Write(Results &results) {}
uint64_t GetValue() const { return m_unsigned; }
protected:
uint64_t m_unsigned;
};
Results() : m_results() {}
~Results() {}
Dictionary &GetDictionary() { return m_results; }
void Write(const char *path);
protected:
Dictionary m_results;
};
} // namespace lldb_perf
#endif // #ifndef __PerfTestDriver_Results_h__

View File

@@ -0,0 +1,308 @@
//===-- TestCase.cpp --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestCase.h"
#include "Results.h"
#include "Xcode.h"
using namespace lldb_perf;
TestCase::TestCase()
: m_debugger(), m_target(), m_process(), m_thread(), m_listener(),
m_verbose(false), m_step(0) {
SBDebugger::Initialize();
SBHostOS::ThreadCreated("<lldb-tester.app.main>");
m_debugger = SBDebugger::Create(false);
m_listener = m_debugger.GetListener();
m_listener.StartListeningForEventClass(
m_debugger, SBProcess::GetBroadcasterClass(),
SBProcess::eBroadcastBitStateChanged | SBProcess::eBroadcastBitInterrupt);
}
static 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;
}
bool TestCase::Setup(int &argc, const char **&argv) {
bool done = false;
struct option *long_options = GetLongOptions();
if (long_options) {
std::string short_option_string(GetShortOptionString(long_options));
#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<char **>(argv), short_option_string.c_str(),
long_options, &long_options_index);
switch (short_option) {
case 0:
// Already handled
break;
case -1:
done = true;
break;
default:
done = !ParseOption(short_option, optarg);
break;
}
}
argc -= optind;
argv += optind;
}
return false;
}
bool TestCase::Launch(lldb::SBLaunchInfo &launch_info) {
lldb::SBError error;
m_process = m_target.Launch(launch_info, error);
if (!error.Success())
fprintf(stderr, "error: %s\n", error.GetCString());
if (m_process.IsValid())
return true;
return false;
}
bool TestCase::Launch(std::initializer_list<const char *> args) {
std::vector<const char *> args_vect(args);
args_vect.push_back(NULL);
lldb::SBLaunchInfo launch_info((const char **)&args_vect[0]);
return Launch(launch_info);
}
void TestCase::SetVerbose(bool b) { m_verbose = b; }
bool TestCase::GetVerbose() { return m_verbose; }
void TestCase::Loop() {
while (true) {
bool call_test_step = false;
if (m_process.IsValid()) {
SBEvent evt;
m_listener.WaitForEvent(UINT32_MAX, evt);
StateType state = SBProcess::GetStateFromEvent(evt);
if (m_verbose)
printf("event = %s\n", SBDebugger::StateAsCString(state));
if (SBProcess::GetRestartedFromEvent(evt)) {
if (m_verbose) {
const uint32_t num_threads = m_process.GetNumThreads();
for (auto thread_index = 0; thread_index < num_threads;
thread_index++) {
SBThread thread(m_process.GetThreadAtIndex(thread_index));
SBFrame frame(thread.GetFrameAtIndex(0));
SBStream strm;
strm.RedirectToFileHandle(stdout, false);
frame.GetDescription(strm);
}
puts("restarted");
}
call_test_step = false;
} else {
switch (state) {
case eStateInvalid:
case eStateDetached:
case eStateCrashed:
case eStateUnloaded:
break;
case eStateExited:
return;
case eStateConnected:
case eStateAttaching:
case eStateLaunching:
case eStateRunning:
case eStateStepping:
call_test_step = false;
break;
case eStateStopped:
case eStateSuspended: {
call_test_step = true;
bool fatal = false;
bool selected_thread = false;
const uint32_t num_threads = m_process.GetNumThreads();
for (auto thread_index = 0; thread_index < num_threads;
thread_index++) {
SBThread thread(m_process.GetThreadAtIndex(thread_index));
SBFrame frame(thread.GetFrameAtIndex(0));
SBStream strm;
strm.RedirectToFileHandle(stdout, false);
frame.GetDescription(strm);
bool select_thread = false;
StopReason stop_reason = thread.GetStopReason();
if (m_verbose)
printf("tid = 0x%llx pc = 0x%llx ", thread.GetThreadID(),
frame.GetPC());
switch (stop_reason) {
case eStopReasonNone:
if (m_verbose)
printf("none\n");
break;
case eStopReasonTrace:
select_thread = true;
if (m_verbose)
printf("trace\n");
break;
case eStopReasonPlanComplete:
select_thread = true;
if (m_verbose)
printf("plan complete\n");
break;
case eStopReasonThreadExiting:
if (m_verbose)
printf("thread exiting\n");
break;
case eStopReasonExec:
if (m_verbose)
printf("exec\n");
break;
case eStopReasonInvalid:
if (m_verbose)
printf("invalid\n");
break;
case eStopReasonException:
select_thread = true;
if (m_verbose)
printf("exception\n");
fatal = true;
break;
case eStopReasonBreakpoint:
select_thread = true;
if (m_verbose)
printf("breakpoint id = %lld.%lld\n",
thread.GetStopReasonDataAtIndex(0),
thread.GetStopReasonDataAtIndex(1));
break;
case eStopReasonWatchpoint:
select_thread = true;
if (m_verbose)
printf("watchpoint id = %lld\n",
thread.GetStopReasonDataAtIndex(0));
break;
case eStopReasonSignal:
select_thread = true;
if (m_verbose)
printf("signal %d\n", (int)thread.GetStopReasonDataAtIndex(0));
break;
}
if (select_thread && !selected_thread) {
m_thread = thread;
selected_thread = m_process.SetSelectedThread(thread);
}
}
if (fatal) {
if (m_verbose)
Xcode::RunCommand(m_debugger, "bt all", true);
exit(1);
}
} break;
}
}
} else {
call_test_step = true;
}
if (call_test_step) {
do_the_call:
if (m_verbose)
printf("RUNNING STEP %d\n", m_step);
ActionWanted action;
TestStep(m_step, action);
m_step++;
SBError err;
switch (action.type) {
case ActionWanted::Type::eNone:
// Just exit and wait for the next event
break;
case ActionWanted::Type::eContinue:
err = m_process.Continue();
break;
case ActionWanted::Type::eStepOut:
if (action.thread.IsValid() == false) {
if (m_verbose) {
Xcode::RunCommand(m_debugger, "bt all", true);
printf("error: invalid thread for step out on step %d\n", m_step);
}
exit(501);
}
m_process.SetSelectedThread(action.thread);
action.thread.StepOut();
break;
case ActionWanted::Type::eStepOver:
if (action.thread.IsValid() == false) {
if (m_verbose) {
Xcode::RunCommand(m_debugger, "bt all", true);
printf("error: invalid thread for step over %d\n", m_step);
}
exit(500);
}
m_process.SetSelectedThread(action.thread);
action.thread.StepOver();
break;
case ActionWanted::Type::eRelaunch:
if (m_process.IsValid()) {
m_process.Kill();
m_process.Clear();
}
Launch(action.launch_info);
break;
case ActionWanted::Type::eKill:
if (m_verbose)
printf("kill\n");
m_process.Kill();
return;
case ActionWanted::Type::eCallNext:
goto do_the_call;
break;
}
}
}
if (GetVerbose())
printf("I am gonna die at step %d\n", m_step);
}
int TestCase::Run(TestCase &test, int argc, const char **argv) {
if (test.Setup(argc, argv)) {
test.Loop();
Results results;
test.WriteResults(results);
return RUN_SUCCESS;
} else
return RUN_SETUP_ERROR;
}

Some files were not shown because too many files have changed in this diff Show More