Commit fb62db02 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge branch 'master' into HEAD

parents f2632e45 0defcec2
......@@ -112,12 +112,25 @@ ifeq ($(NEED_OLD_JIT),1)
LLVM_LINK_LIBS += jit
endif
LLVM_CONFIG_DBG := $(LLVM_BUILD)/Release+Asserts/bin/llvm-config
ifneq ($(wildcard $(LLVM_CONFIG_DBG)),)
LLVM_CXXFLAGS := $(shell $(LLVM_BUILD)/Release+Asserts/bin/llvm-config --cxxflags)
LLVM_LDFLAGS := $(shell $(LLVM_BUILD)/Release+Asserts/bin/llvm-config --ldflags --system-libs --libs $(LLVM_LINK_LIBS))
LLVM_LIB_DEPS := $(wildcard $(LLVM_BUILD)/Release+Asserts/lib/*)
else
LLVM_CXXFLAGS := DBG_NOT_BUILT
LLVM_LDFLAGS := DBG_NOT_BUILT
LLVM_LIB_DEPS := DBG_NOT_BUILT
endif
LLVM_CONFIG_DEBUG := $(LLVM_BUILD)/Debug+Asserts/bin/llvm-config
ifneq ($(wildcard $(LLVM_CONFIG_DBG)),)
LLVM_DEBUG_LDFLAGS := $(shell $(LLVM_BUILD)/Debug+Asserts/bin/llvm-config --ldflags --system-libs --libs $(LLVM_LINK_LIBS))
LLVM_DEBUG_LIB_DEPS := $(wildcard $(LLVM_BUILD)/Debug+Asserts/lib/*)
else
LLVM_DEBUG_LDFLAGS := DEBUG_NOT_BUILT
LLVM_DEBUG_LIB_DEPS := DEBUG_NOT_BUILT
endif
LLVM_CONFIG_RELEASE := $(LLVM_BUILD)/Release/bin/llvm-config
ifneq ($(wildcard $(LLVM_CONFIG_RELEASE)),)
......@@ -779,11 +792,16 @@ endef
PASS_SRCS := codegen/opt/aa.cpp
PASS_OBJS := $(PASS_SRCS:.cpp=.standalone.o)
ifneq ($(USE_CMAKE),1)
$(call make_compile_config,,$(CXXFLAGS_DBG))
$(call make_compile_config,.release,$(CXXFLAGS_RELEASE))
$(call make_compile_config,.grwl,$(CXXFLAGS_RELEASE) -DTHREADING_USE_GRWL=1 -DTHREADING_USE_GIL=0 -UBINARY_SUFFIX -DBINARY_SUFFIX=_grwl)
$(call make_compile_config,.grwl_dbg,$(CXXFLAGS_DBG) -DTHREADING_USE_GRWL=1 -DTHREADING_USE_GIL=0 -UBINARY_SUFFIX -DBINARY_SUFFIX=_grwl_dbg -UBINARY_STRIPPED_SUFFIX -DBINARY_STRIPPED_SUFFIX=)
$(call make_compile_config,.nosync,$(CXXFLAGS_RELEASE) -DTHREADING_USE_GRWL=0 -DTHREADING_USE_GIL=0 -UBINARY_SUFFIX -DBINARY_SUFFIX=_nosync)
else
%.o: %.cpp $(CMAKE_SETUP_DBG)
$(NINJA) -C $(HOME)/pyston-build-dbg src/CMakeFiles/PYSTON_OBJECTS.dir/$(patsubst src/%.o,%.cpp.o,$@) $(NINJAFLAGS)
endif
$(UNITTEST_SRCS:.cpp=.o): CXXFLAGS += -isystem $(GTEST_DIR)/include
......
......@@ -61,6 +61,9 @@
#define HAVE_STRUCT_TM_TM_ZONE 1
#define HAVE_MKTIME 1
#define TIME_WITH_SYS_TIME
#define HAVE_GETTIMEOFDAY 1
#define PY_FORMAT_LONG_LONG "ll"
#define PY_FORMAT_SIZE_T "z"
......
from django.template.base import Origin, Template, Context, TemplateDoesNotExist
from django.conf import settings
from django.apps import apps
import time
try:
import __pyston__
pyston_loaded = True
except:
pyston_loaded = False
template_source = """
{% extends "admin/base_site.html" %}
{% load i18n admin_static %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}" />{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block bodyclass %}{{ block.super }} dashboard{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content %}
<div id="content-main">
{% if app_list %}
{% for app in app_list %}
<div class="app-{{ app.app_label }} module">
<table>
<caption>
<a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a>
</caption>
{% for model in app.models %}
<tr class="model-{{ model.object_name|lower }}">
{% if model.admin_url %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.add_url %}
<td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
{% if model.admin_url %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endfor %}
{% else %}
<p>{% trans "You don't have permission to edit anything." %}</p>
{% endif %}
</div>
{% endblock %}
{% block sidebar %}
<div id="content-related">
<div class="module" id="recent-actions-module">
<h2>{% trans 'Recent Actions' %}</h2>
<h3>{% trans 'My Actions' %}</h3>
{% load log %}
{% get_admin_log 10 as admin_log for_user user %}
{% if not admin_log %}
<p>{% trans 'None available' %}</p>
{% else %}
<ul class="actionlist">
{% for entry in admin_log %}
<li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">
{% if entry.is_deletion or not entry.get_admin_url %}
{{ entry.object_repr }}
{% else %}
<a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
{% endif %}
<br/>
{% if entry.content_type %}
<span class="mini quiet">{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}</span>
{% else %}
<span class="mini quiet">{% trans 'Unknown content' %}</span>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}
"""
settings.configure()
apps.populate((
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
))
elapsed = 0
for i in xrange(500):
#if pyston_loaded:
# __pyston__.clearStats()
start = time.time()
template = Template(template_source, None, "admin/index.html")
elapsed = time.time() - start
print "took %4.1fms for last iteration" % (elapsed * 1000.0,)
......@@ -76,14 +76,21 @@ private:
}
}
Status::Usage getStatusFirst(InternedString name) const {
auto it = statuses.find(name);
if (it == statuses.end())
return Status::NONE;
return it->second.first;
}
public:
LivenessBBVisitor(LivenessAnalysis* analysis) : analysis(analysis) {}
bool firstIsUse(InternedString name) { return statuses[name].first == Status::USED; }
bool firstIsUse(InternedString name) const { return getStatusFirst(name) == Status::USED; }
bool firstIsDef(InternedString name) { return statuses[name].first == Status::DEFINED; }
bool firstIsDef(InternedString name) const { return getStatusFirst(name) == Status::DEFINED; }
bool isKilledAt(AST_Name* node, bool is_live_at_end) {
bool isKilledAt(AST_Name* node, bool is_live_at_end) const {
if (kills.count(node))
return true;
......@@ -163,6 +170,9 @@ LivenessAnalysis::LivenessAnalysis(CFG* cfg) : cfg(cfg) {
us_liveness.log(_t.end());
}
LivenessAnalysis::~LivenessAnalysis() {
}
bool LivenessAnalysis::isKill(AST_Name* node, CFGBlock* parent_block) {
if (node->id.str()[0] != '#')
return false;
......@@ -487,11 +497,18 @@ bool PhiAnalysis::isPotentiallyUndefinedAt(InternedString name, CFGBlock* block)
return definedness.defined_at_beginning[block][name] != DefinednessAnalysis::Defined;
}
LivenessAnalysis* computeLivenessInfo(CFG* cfg) {
return new LivenessAnalysis(cfg);
std::unique_ptr<LivenessAnalysis> computeLivenessInfo(CFG* cfg) {
static StatCounter counter("num_liveness_analysis");
counter.log();
return std::unique_ptr<LivenessAnalysis>(new LivenessAnalysis(cfg));
}
PhiAnalysis* computeRequiredPhis(const ParamNames& args, CFG* cfg, LivenessAnalysis* liveness, ScopeInfo* scope_info) {
std::unique_ptr<PhiAnalysis> computeRequiredPhis(const ParamNames& args, CFG* cfg, LivenessAnalysis* liveness,
ScopeInfo* scope_info) {
static StatCounter counter("num_phi_analysis");
counter.log();
llvm::DenseMap<InternedString, DefinednessAnalysis::DefinitionLevel> initial_map;
for (auto e : args.args)
......@@ -501,11 +518,15 @@ PhiAnalysis* computeRequiredPhis(const ParamNames& args, CFG* cfg, LivenessAnaly
if (args.kwarg.size())
initial_map[scope_info->internString(args.kwarg)] = DefinednessAnalysis::Defined;
return new PhiAnalysis(std::move(initial_map), cfg->getStartingBlock(), false, liveness, scope_info);
return std::unique_ptr<PhiAnalysis>(
new PhiAnalysis(std::move(initial_map), cfg->getStartingBlock(), false, liveness, scope_info));
}
PhiAnalysis* computeRequiredPhis(const OSREntryDescriptor* entry_descriptor, LivenessAnalysis* liveness,
ScopeInfo* scope_info) {
std::unique_ptr<PhiAnalysis> computeRequiredPhis(const OSREntryDescriptor* entry_descriptor, LivenessAnalysis* liveness,
ScopeInfo* scope_info) {
static StatCounter counter("num_phi_analysis");
counter.log();
llvm::DenseMap<InternedString, DefinednessAnalysis::DefinitionLevel> initial_map;
llvm::StringSet<> potentially_undefined;
......@@ -524,6 +545,7 @@ PhiAnalysis* computeRequiredPhis(const OSREntryDescriptor* entry_descriptor, Liv
initial_map[p.first] = DefinednessAnalysis::Defined;
}
return new PhiAnalysis(std::move(initial_map), entry_descriptor->backedge->target, true, liveness, scope_info);
return std::unique_ptr<PhiAnalysis>(
new PhiAnalysis(std::move(initial_map), entry_descriptor->backedge->target, true, liveness, scope_info));
}
}
......@@ -45,6 +45,7 @@ private:
public:
LivenessAnalysis(CFG* cfg);
~LivenessAnalysis();
// we don't keep track of node->parent_block relationships, so you have to pass both:
bool isKill(AST_Name* node, CFGBlock* parent_block);
......@@ -105,9 +106,9 @@ public:
bool isPotentiallyUndefinedAt(InternedString name, CFGBlock* block);
};
LivenessAnalysis* computeLivenessInfo(CFG*);
PhiAnalysis* computeRequiredPhis(const ParamNames&, CFG*, LivenessAnalysis*, ScopeInfo* scope_info);
PhiAnalysis* computeRequiredPhis(const OSREntryDescriptor*, LivenessAnalysis*, ScopeInfo* scope_info);
std::unique_ptr<LivenessAnalysis> computeLivenessInfo(CFG*);
std::unique_ptr<PhiAnalysis> computeRequiredPhis(const ParamNames&, CFG*, LivenessAnalysis*, ScopeInfo* scope_info);
std::unique_ptr<PhiAnalysis> computeRequiredPhis(const OSREntryDescriptor*, LivenessAnalysis*, ScopeInfo* scope_info);
}
#endif
......@@ -18,6 +18,7 @@
namespace pyston {
Box* BoxedMethodDescriptor::__call__(BoxedMethodDescriptor* self, Box* obj, BoxedTuple* varargs, Box** _args) {
STAT_TIMER(t0, "us_timer_boxedmethoddescriptor__call__");
BoxedDict* kwargs = static_cast<BoxedDict*>(_args[0]);
assert(self->cls == method_cls);
......
......@@ -58,6 +58,7 @@ static int hackcheck(PyObject* self, setattrofunc func, const char* what) noexce
}
static PyObject* wrap_setattr(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_setattr");
setattrofunc func = (setattrofunc)wrapped;
int res;
PyObject* name, *value;
......@@ -74,6 +75,7 @@ static PyObject* wrap_setattr(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_delattr(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_delattr");
setattrofunc func = (setattrofunc)wrapped;
int res;
PyObject* name;
......@@ -91,6 +93,7 @@ static PyObject* wrap_delattr(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_hashfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_hashfunc");
hashfunc func = (hashfunc)wrapped;
long res;
......@@ -103,12 +106,14 @@ static PyObject* wrap_hashfunc(PyObject* self, PyObject* args, void* wrapped) no
}
static PyObject* wrap_call(PyObject* self, PyObject* args, void* wrapped, PyObject* kwds) noexcept {
STAT_TIMER(t0, "us_timer_wrap_call");
ternaryfunc func = (ternaryfunc)wrapped;
return (*func)(self, args, kwds);
}
static PyObject* wrap_richcmpfunc(PyObject* self, PyObject* args, void* wrapped, int op) noexcept {
STAT_TIMER(t0, "us_timer_wrap_richcmpfunc");
richcmpfunc func = (richcmpfunc)wrapped;
PyObject* other;
......@@ -132,6 +137,7 @@ RICHCMP_WRAPPER(gt, Py_GT)
RICHCMP_WRAPPER(ge, Py_GE)
static PyObject* wrap_next(PyObject* self, PyObject* args, void* wrapped) {
STAT_TIMER(t0, "us_timer_wrap_next");
unaryfunc func = (unaryfunc)wrapped;
PyObject* res;
......@@ -144,6 +150,7 @@ static PyObject* wrap_next(PyObject* self, PyObject* args, void* wrapped) {
}
static PyObject* wrap_descr_get(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_descr_get");
descrgetfunc func = (descrgetfunc)wrapped;
PyObject* obj;
PyObject* type = NULL;
......@@ -162,6 +169,7 @@ static PyObject* wrap_descr_get(PyObject* self, PyObject* args, void* wrapped) n
}
static PyObject* wrap_coercefunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_coercefunc");
coercion func = (coercion)wrapped;
PyObject* other, *res;
int ok;
......@@ -188,6 +196,7 @@ static PyObject* wrap_coercefunc(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_ternaryfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_ternaryfunc");
ternaryfunc func = (ternaryfunc)wrapped;
PyObject* other;
PyObject* third = Py_None;
......@@ -200,6 +209,7 @@ static PyObject* wrap_ternaryfunc(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_ternaryfunc_r(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_ternaryfunc_r");
ternaryfunc func = (ternaryfunc)wrapped;
PyObject* other;
PyObject* third = Py_None;
......@@ -212,6 +222,7 @@ static PyObject* wrap_ternaryfunc_r(PyObject* self, PyObject* args, void* wrappe
}
static PyObject* wrap_unaryfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_unaryfunc");
unaryfunc func = (unaryfunc)wrapped;
if (!check_num_args(args, 0))
......@@ -220,6 +231,7 @@ static PyObject* wrap_unaryfunc(PyObject* self, PyObject* args, void* wrapped) n
}
static PyObject* wrap_inquirypred(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_inquirypred");
inquiry func = (inquiry)wrapped;
int res;
......@@ -232,6 +244,7 @@ static PyObject* wrap_inquirypred(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrapInquirypred(PyObject* self, PyObject* args, void* wrapped) {
STAT_TIMER(t0, "us_timer_wrapInquirypred");
inquiry func = (inquiry)wrapped;
int res;
......@@ -244,6 +257,7 @@ static PyObject* wrapInquirypred(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_binaryfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_binaryfunc");
binaryfunc func = (binaryfunc)wrapped;
PyObject* other;
......@@ -254,6 +268,7 @@ static PyObject* wrap_binaryfunc(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_binaryfunc_l(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_binaryfunc_l");
binaryfunc func = (binaryfunc)wrapped;
PyObject* other;
......@@ -268,6 +283,7 @@ static PyObject* wrap_binaryfunc_l(PyObject* self, PyObject* args, void* wrapped
}
static PyObject* wrap_binaryfunc_r(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_binaryfunc_r");
binaryfunc func = (binaryfunc)wrapped;
PyObject* other;
......@@ -300,6 +316,7 @@ static Py_ssize_t getindex(PyObject* self, PyObject* arg) noexcept {
}
static PyObject* wrap_lenfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_lenfunc");
lenfunc func = (lenfunc)wrapped;
Py_ssize_t res;
......@@ -312,6 +329,7 @@ static PyObject* wrap_lenfunc(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_indexargfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_indexargfunc");
ssizeargfunc func = (ssizeargfunc)wrapped;
PyObject* o;
Py_ssize_t i;
......@@ -325,6 +343,7 @@ static PyObject* wrap_indexargfunc(PyObject* self, PyObject* args, void* wrapped
}
static PyObject* wrap_sq_item(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_sq_item");
ssizeargfunc func = (ssizeargfunc)wrapped;
PyObject* arg;
Py_ssize_t i;
......@@ -342,6 +361,7 @@ static PyObject* wrap_sq_item(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_ssizessizeargfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_ssizessizeargfunc");
ssizessizeargfunc func = (ssizessizeargfunc)wrapped;
Py_ssize_t i, j;
......@@ -351,6 +371,7 @@ static PyObject* wrap_ssizessizeargfunc(PyObject* self, PyObject* args, void* wr
}
static PyObject* wrap_sq_setitem(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_sq_setitem");
ssizeobjargproc func = (ssizeobjargproc)wrapped;
Py_ssize_t i;
int res;
......@@ -369,6 +390,7 @@ static PyObject* wrap_sq_setitem(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_sq_delitem(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_sq_delitem");
ssizeobjargproc func = (ssizeobjargproc)wrapped;
Py_ssize_t i;
int res;
......@@ -388,6 +410,7 @@ static PyObject* wrap_sq_delitem(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_ssizessizeobjargproc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_ssizessizeobjargproc");
ssizessizeobjargproc func = (ssizessizeobjargproc)wrapped;
Py_ssize_t i, j;
int res;
......@@ -403,6 +426,7 @@ static PyObject* wrap_ssizessizeobjargproc(PyObject* self, PyObject* args, void*
}
static PyObject* wrap_delslice(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_delslice");
ssizessizeobjargproc func = (ssizessizeobjargproc)wrapped;
Py_ssize_t i, j;
int res;
......@@ -418,6 +442,7 @@ static PyObject* wrap_delslice(PyObject* self, PyObject* args, void* wrapped) no
/* XXX objobjproc is a misnomer; should be objargpred */
static PyObject* wrap_objobjproc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_objobjproc");
objobjproc func = (objobjproc)wrapped;
int res;
PyObject* value;
......@@ -433,6 +458,7 @@ static PyObject* wrap_objobjproc(PyObject* self, PyObject* args, void* wrapped)
}
static PyObject* wrap_objobjargproc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_objobjargproc");
objobjargproc func = (objobjargproc)wrapped;
int res;
PyObject* key, *value;
......@@ -447,6 +473,7 @@ static PyObject* wrap_objobjargproc(PyObject* self, PyObject* args, void* wrappe
}
static PyObject* wrap_delitem(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_delitem");
objobjargproc func = (objobjargproc)wrapped;
int res;
PyObject* key;
......@@ -462,6 +489,7 @@ static PyObject* wrap_delitem(PyObject* self, PyObject* args, void* wrapped) noe
}
static PyObject* wrap_cmpfunc(PyObject* self, PyObject* args, void* wrapped) noexcept {
STAT_TIMER(t0, "us_timer_wrap_cmpfunc");
cmpfunc func = (cmpfunc)wrapped;
int res;
PyObject* other;
......@@ -482,6 +510,7 @@ static PyObject* wrap_cmpfunc(PyObject* self, PyObject* args, void* wrapped) noe
static PyObject* wrap_init(PyObject* self, PyObject* args, void* wrapped, PyObject* kwds) noexcept {
STAT_TIMER(t0, "us_timer_wrap_init");
initproc func = (initproc)wrapped;
if (func(self, args, kwds) < 0)
......
......@@ -54,6 +54,7 @@ public:
}
static Box* __call__(BoxedCApiFunction* self, BoxedTuple* varargs, BoxedDict* kwargs) {
STAT_TIMER(t0, "us_timer_boxedcapifunction__call__");
assert(self->cls == capifunc_cls);
assert(varargs->cls == tuple_cls);
assert(kwargs->cls == dict_cls);
......@@ -111,6 +112,8 @@ public:
DEFAULT_CLASS(wrapperobject_cls);
static Box* __call__(BoxedWrapperObject* self, Box* args, Box* kwds) {
STAT_TIMER(t0, "us_timer_boxedwrapperobject__call__");
assert(self->cls == wrapperobject_cls);
assert(args->cls == tuple_cls);
assert(kwds->cls == dict_cls);
......
......@@ -83,7 +83,6 @@ private:
Value doBinOp(Box* left, Box* right, int op, BinExpType exp_type);
void doStore(AST_expr* node, Value value);
void doStore(InternedString name, Value value);
void eraseDeadSymbols();
Value visit_assert(AST_Assert* node);
Value visit_assign(AST_Assign* node);
......@@ -285,6 +284,8 @@ public:
Value ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block, AST_stmt* start_at) {
threading::allowGLReadPreemption();
STAT_TIMER(t0, "us_timer_astinterpreter_execute");
void* frame_addr = __builtin_frame_address(0);
RegisterHelper frame_registerer(&interpreter, frame_addr);
......@@ -321,31 +322,6 @@ Value ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block
return v;
}
void ASTInterpreter::eraseDeadSymbols() {
if (source_info->liveness == NULL)
source_info->liveness = computeLivenessInfo(source_info->cfg);
if (this->phis == NULL) {
PhiAnalysis*& phis = source_info->phis[/* entry_descriptor = */ NULL];
if (!phis)
phis = computeRequiredPhis(compiled_func->clfunc->param_names, source_info->cfg, source_info->liveness,
scope_info);
this->phis = phis;
}
std::vector<InternedString> dead_symbols;
for (auto& it : sym_table) {
if (!source_info->liveness->isLiveAtEnd(it.first, current_block)) {
dead_symbols.push_back(it.first);
} else if (phis->isRequiredAfter(it.first, current_block)) {
assert(scope_info->getScopeTypeOfName(it.first) != ScopeInfo::VarScopeType::GLOBAL);
} else {
}
}
for (auto&& dead : dead_symbols)
sym_table.erase(dead);
}
Value ASTInterpreter::doBinOp(Box* left, Box* right, int op, BinExpType exp_type) {
if (op == AST_TYPE::Div && (source_info->parent_module->future_flags & FF_DIVISION)) {
op = AST_TYPE::TrueDiv;
......@@ -463,7 +439,26 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
if (ENABLE_OSR && backedge && (globals->cls == module_cls)) {
bool can_osr = !FORCE_INTERPRETER && (globals->cls == module_cls);
if (can_osr && edgecount++ == OSR_THRESHOLD_INTERPRETER) {
eraseDeadSymbols();
static StatCounter ast_osrs("num_ast_osrs");
ast_osrs.log();
// TODO: we will immediately want the liveness info again in the jit, we should pass
// it through.
std::unique_ptr<LivenessAnalysis> liveness = computeLivenessInfo(source_info->cfg);
std::unique_ptr<PhiAnalysis> phis
= computeRequiredPhis(compiled_func->clfunc->param_names, source_info->cfg, liveness.get(), scope_info);
std::vector<InternedString> dead_symbols;
for (auto& it : sym_table) {
if (!liveness->isLiveAtEnd(it.first, current_block)) {
dead_symbols.push_back(it.first);
} else if (phis->isRequiredAfter(it.first, current_block)) {
assert(scope_info->getScopeTypeOfName(it.first) != ScopeInfo::VarScopeType::GLOBAL);
} else {
}
}
for (auto&& dead : dead_symbols)
sym_table.erase(dead);
const OSREntryDescriptor* found_entry = nullptr;
for (auto& p : compiled_func->clfunc->osr_versions) {
......@@ -479,7 +474,7 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
for (auto& name : phis->definedness.getDefinedNamesAtEnd(current_block)) {
auto it = sym_table.find(name);
if (!source_info->liveness->isLiveAtEnd(name, current_block))
if (!liveness->isLiveAtEnd(name, current_block))
continue;
if (phis->isPotentiallyUndefinedAfter(name, current_block)) {
......@@ -495,10 +490,14 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
}
}
// Manually free these here, since we might not return from this scope for a long time.
liveness.reset(nullptr);
phis.reset(nullptr);
// LLVM has a limit on the number of operands a machine instruction can have (~255),
// in order to not hit the limit with the patchpoints cancel OSR when we have a high number of symbols.
if (sorted_symbol_table.size() > 225) {
static StatCounter times_osr_cancel("num_osr_cancel_to_many_syms");
static StatCounter times_osr_cancel("num_osr_cancel_too_many_syms");
times_osr_cancel.log();
next_block = node->target;
return Value();
......@@ -543,6 +542,7 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
arg_array.push_back(it.second);
}
STAT_TIMER(t0, "us_timer_astinterpreter_jump_osrexit");
CompiledFunction* partial_func = compilePartialFuncInternal(&exit);
auto arg_tuple = getTupleFromArgsArray(&arg_array[0], arg_array.size());
Box* r = partial_func->call(std::get<0>(arg_tuple), std::get<1>(arg_tuple), std::get<2>(arg_tuple),
......@@ -930,6 +930,8 @@ Value ASTInterpreter::visit_print(AST_Print* node) {
static const std::string newline_str("\n");
static const std::string space_str(" ");
STAT_TIMER(t0, "us_timer_visit_print");
Box* dest = node->dest ? visit_expr(node->dest).o : getSysStdout();
int nvals = node->values.size();
assert(nvals <= 1 && "cfg should have lowered it to 0 or 1 values");
......
......@@ -35,8 +35,7 @@ namespace pyston {
DS_DEFINE_RWLOCK(codegen_rwlock);
SourceInfo::SourceInfo(BoxedModule* m, ScopingAnalysis* scoping, AST* ast, std::vector<AST_stmt*> body, std::string fn)
: parent_module(m), scoping(scoping), ast(ast), cfg(NULL), liveness(NULL), fn(std::move(fn)),
body(std::move(body)) {
: parent_module(m), scoping(scoping), ast(ast), cfg(NULL), fn(std::move(fn)), body(std::move(body)) {
assert(this->fn.size());
switch (ast->type) {
......
......@@ -362,7 +362,7 @@ static void handle_sigint(int signum) {
// For now, just call abort(), so that we get a traceback at least.
fprintf(stderr, "SIGINT!\n");
joinRuntime();
Stats::dump();
Stats::dump(false);
abort();
}
......
......@@ -342,7 +342,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
CompiledFunction* cf = irstate->getCurFunction();
ConcreteCompilerType* rtn_type = irstate->getReturnType();
// llvm::MDNode* func_info = irstate->getFuncDbgInfo();
PhiAnalysis* phi_analysis = source->phis[entry_descriptor];
PhiAnalysis* phi_analysis = irstate->getPhis();
assert(phi_analysis);
if (entry_descriptor != NULL)
......@@ -1055,7 +1055,15 @@ CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const O
computeBlockSetClosure(blocks);
}
IRGenState irstate(cf, source, source->phis[entry_descriptor], param_names, getGCBuilder(), dbg_funcinfo);
std::unique_ptr<LivenessAnalysis> liveness = computeLivenessInfo(source->cfg);
std::unique_ptr<PhiAnalysis> phis;
if (entry_descriptor)
phis = computeRequiredPhis(entry_descriptor, liveness.get(), source->getScopeInfo());
else
phis = computeRequiredPhis(*param_names, source->cfg, liveness.get(), source->getScopeInfo());
IRGenState irstate(cf, source, std::move(liveness), std::move(phis), param_names, getGCBuilder(), dbg_funcinfo);
emitBBs(&irstate, types, entry_descriptor, blocks);
......
......@@ -182,6 +182,7 @@ static void compileIR(CompiledFunction* cf, EffortLevel effort) {
// The codegen_lock needs to be held in W mode before calling this function:
CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, EffortLevel effort,
const OSREntryDescriptor* entry_descriptor) {
STAT_TIMER(t0, "us_timer_compileFunction");
Timer _t("for compileFunction()", 1000);
assert((entry_descriptor != NULL) + (spec != NULL) == 1);
......@@ -228,19 +229,6 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
source->cfg = computeCFG(source, source->body);
}
if (effort != EffortLevel::INTERPRETED) {
if (source->liveness == NULL)
source->liveness = computeLivenessInfo(source->cfg);
PhiAnalysis*& phis = source->phis[entry_descriptor];
if (!phis) {
if (entry_descriptor)
phis = computeRequiredPhis(entry_descriptor, source->liveness, source->getScopeInfo());
else
phis = computeRequiredPhis(f->param_names, source->cfg, source->liveness, source->getScopeInfo());
}
}
CompiledFunction* cf = 0;
......@@ -328,10 +316,13 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
assert(cf->clfunc->versions.size());
}
if (cf->is_interpreted)
if (cf->is_interpreted) {
STAT_TIMER(t0, "us_timer_interpreted_module_toplevel");
astInterpretFunction(cf, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
else
} else {
STAT_TIMER(t1, "us_timer_jitted_module_toplevel");
((void (*)())cf->code)();
}
}
Box* evalOrExec(CLFunction* cl, Box* globals, Box* boxedLocals) {
......
......@@ -42,6 +42,19 @@ extern "C" void dumpLLVM(llvm::Value* v) {
v->dump();
}
IRGenState::IRGenState(CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<LivenessAnalysis> liveness,
std::unique_ptr<PhiAnalysis> phis, ParamNames* param_names, GCBuilder* gc,
llvm::MDNode* func_dbg_info)
: cf(cf), source_info(source_info), liveness(std::move(liveness)), phis(std::move(phis)), param_names(param_names),
gc(gc), func_dbg_info(func_dbg_info), scratch_space(NULL), frame_info(NULL), frame_info_arg(NULL),
scratch_size(0) {
assert(cf->func);
assert(!cf->clfunc); // in this case don't need to pass in sourceinfo
}
IRGenState::~IRGenState() {
}
llvm::Value* IRGenState::getScratchSpace(int min_bytes) {
llvm::BasicBlock& entry_block = getLLVMFunction()->getEntryBlock();
......@@ -944,7 +957,7 @@ private:
CompilerVariable* evalName(AST_Name* node, UnwindInfo unw_info) {
auto scope_info = irstate->getScopeInfo();
bool is_kill = irstate->getSourceInfo()->liveness->isKill(node, myblock);
bool is_kill = irstate->getLiveness()->isKill(node, myblock);
assert(!is_kill || node->id.str()[0] == '#');
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(node->id);
......@@ -2271,7 +2284,7 @@ private:
// ASSERT(p.first[0] != '!' || isIsDefinedName(p.first), "left a fake variable in the real
// symbol table? '%s'", p.first.c_str());
if (!source->liveness->isLiveAtEnd(p.first, myblock)) {
if (!irstate->getLiveness()->isLiveAtEnd(p.first, myblock)) {
// printf("%s dead at end of %d; grabbed = %d, %d vrefs\n", p.first.c_str(), myblock->idx,
// p.second->isGrabbed(), p.second->getVrefs());
......
......@@ -56,7 +56,8 @@ class IRGenState {
private:
CompiledFunction* cf;
SourceInfo* source_info;
PhiAnalysis* phis;
std::unique_ptr<LivenessAnalysis> liveness;
std::unique_ptr<PhiAnalysis> phis;
ParamNames* param_names;
GCBuilder* gc;
llvm::MDNode* func_dbg_info;
......@@ -69,13 +70,9 @@ private:
public:
IRGenState(CompiledFunction* cf, SourceInfo* source_info, PhiAnalysis* phis, ParamNames* param_names, GCBuilder* gc,
llvm::MDNode* func_dbg_info)
: cf(cf), source_info(source_info), phis(phis), param_names(param_names), gc(gc), func_dbg_info(func_dbg_info),
scratch_space(NULL), frame_info(NULL), frame_info_arg(NULL), scratch_size(0) {
assert(cf->func);
assert(!cf->clfunc); // in this case don't need to pass in sourceinfo
}
IRGenState(CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<LivenessAnalysis> liveness,
std::unique_ptr<PhiAnalysis> phis, ParamNames* param_names, GCBuilder* gc, llvm::MDNode* func_dbg_info);
~IRGenState();
CompiledFunction* getCurFunction() { return cf; }
......@@ -93,7 +90,8 @@ public:
SourceInfo* getSourceInfo() { return source_info; }
PhiAnalysis* getPhis() { return phis; }
LivenessAnalysis* getLiveness() { return liveness.get(); }
PhiAnalysis* getPhis() { return phis.get(); }
ScopeInfo* getScopeInfo();
ScopeInfo* getScopeInfoForNode(AST* node);
......
......@@ -982,6 +982,7 @@ AST_Module* parse_string(const char* code) {
}
AST_Module* parse_file(const char* fn) {
STAT_TIMER(t0, "us_timer_cpyton_parsing");
Timer _t("parsing");
if (ENABLE_PYPA_PARSER) {
......@@ -1071,9 +1072,10 @@ static ParseResult _reparse(const char* fn, const std::string& cache_fn, AST_Mod
// it's not a huge deal right now, but this caching version can significantly cut down
// on the startup time (40ms -> 10ms).
AST_Module* caching_parse_file(const char* fn) {
STAT_TIMER(t0, "us_timer_caching_parse_file");
static StatCounter us_parsing("us_parsing");
Timer _t("parsing");
_t.setExitCallback([](long t) { us_parsing.log(t); });
_t.setExitCallback([](uint64_t t) { us_parsing.log(t); });
int code;
std::string cache_fn = std::string(fn) + "c";
......
......@@ -494,6 +494,7 @@ void unwindPythonStack(std::function<bool(std::unique_ptr<PythonFrameIteratorImp
}
static std::unique_ptr<PythonFrameIteratorImpl> getTopPythonFrame() {
STAT_TIMER(t0, "us_timer_getTopPythonFrame");
std::unique_ptr<PythonFrameIteratorImpl> rtn(nullptr);
unwindPythonStack([&](std::unique_ptr<PythonFrameIteratorImpl> iter) {
rtn = std::move(iter);
......@@ -514,6 +515,7 @@ static const LineInfo* lineInfoForFrame(PythonFrameIteratorImpl& frame_it) {
static StatCounter us_gettraceback("us_gettraceback");
BoxedTraceback* getTraceback() {
STAT_TIMER(t0, "us_timer_gettraceback");
if (!ENABLE_FRAME_INTROSPECTION) {
static bool printed_warning = false;
if (!printed_warning) {
......
......@@ -17,13 +17,115 @@
#include <algorithm>
#include "core/thread_utils.h"
#include "gc/heap.h"
namespace pyston {
#if !DISABLE_STATS
std::vector<long>* Stats::counts;
#if STAT_TIMERS
extern "C" const char* getStatTimerNameById(int id) {
return Stats::getStatName(id).c_str();
}
extern "C" int getStatTimerId() {
return StatTimer::getStack()->getId();
}
extern "C" const char* getStatTimerName() {
return getStatTimerNameById(getStatTimerId());
}
__thread StatTimer* StatTimer::stack;
StatTimer::StatTimer(int statid, bool push) {
uint64_t at_time = getCPUTicks();
_duration = 0;
_start_time = 0;
_statid = statid;
if (!push) {
_prev = NULL;
return;
}
_prev = stack;
stack = this;
if (_prev) {
_prev->pause(at_time);
}
resume(at_time);
}
StatTimer::StatTimer(int statid, uint64_t at_time) {
_duration = 0;
_start_time = 0;
_statid = statid;
_prev = stack;
stack = this;
if (_prev) {
_prev->pause(at_time);
}
resume(at_time);
}
StatTimer::~StatTimer() {
assert(stack == this);
uint64_t at_time;
if (!isPaused()) {
at_time = getCPUTicks();
pause(at_time);
} else {
// fprintf (stderr, "WARNING: timer was paused.\n");
at_time = _last_pause_time;
}
stack = _prev;
if (stack) {
stack->resume(at_time);
}
}
void StatTimer::pause(uint64_t at_time) {
assert(!isPaused());
assert(at_time > _start_time);
auto cur_duration = _duration;
_duration = at_time - _start_time;
assert(_duration > cur_duration);
Stats::log(_statid, _duration);
_duration = 0;
_start_time = 0;
_last_pause_time = at_time;
// fprintf (stderr, "paused %d at %lu\n", _statid, at_time);
}
void StatTimer::resume(uint64_t at_time) {
assert(isPaused());
_start_time = at_time;
// fprintf (stderr, "resumed %d at %lu\n", _statid, at_time);
}
StatTimer* StatTimer::swapStack(StatTimer* s, uint64_t at_time) {
StatTimer* prev_stack = stack;
if (stack) {
stack->pause(at_time);
}
stack = s;
if (stack) {
stack->resume(at_time);
}
return prev_stack;
}
#endif
std::vector<uint64_t>* Stats::counts;
std::unordered_map<int, std::string>* Stats::names;
bool Stats::enabled;
timespec Stats::start_ts;
uint64_t Stats::start_tick;
StatCounter::StatCounter(const std::string& name) : id(Stats::getStatId(name)) {
}
......@@ -37,7 +139,7 @@ int Stats::getStatId(const std::string& name) {
// hacky but easy way of getting around static constructor ordering issues for now:
static std::unordered_map<int, std::string> names;
Stats::names = &names;
static std::vector<long> counts;
static std::vector<uint64_t> counts;
Stats::counts = &counts;
static std::unordered_map<std::string, int> made;
......@@ -51,11 +153,40 @@ int Stats::getStatId(const std::string& name) {
return rtn;
}
std::string Stats::getStatName(int id) {
return (*names)[id];
}
void Stats::startEstimatingCPUFreq() {
if (!Stats::enabled)
return;
clock_gettime(CLOCK_REALTIME, &Stats::start_ts);
Stats::start_tick = getCPUTicks();
}
// returns our estimate of the MHz of the cpu. MHz is handy because we're mostly interested in microsoecond-resolution
// timing.
double Stats::estimateCPUFreq() {
timespec dump_ts;
clock_gettime(CLOCK_REALTIME, &dump_ts);
uint64_t end_tick = getCPUTicks();
uint64_t wall_clock_ns = (dump_ts.tv_sec - start_ts.tv_sec) * 1000000000 + (dump_ts.tv_nsec - start_ts.tv_nsec);
return (double)(end_tick - Stats::start_tick) * 1000 / wall_clock_ns;
}
void Stats::dump(bool includeZeros) {
if (!Stats::enabled)
return;
printf("Stats:\n");
double cycles_per_us = Stats::estimateCPUFreq();
fprintf(stderr, "Stats:\n");
fprintf(stderr, "estimated_cpu_mhz: %5.5f\n", cycles_per_us);
gc::dumpHeapStatistics(0);
fprintf(stderr, "Counters:\n");
std::vector<std::pair<std::string, int>> pairs;
for (const auto& p : *names) {
......@@ -64,10 +195,42 @@ void Stats::dump(bool includeZeros) {
std::sort(pairs.begin(), pairs.end());
uint64_t ticks_in_main = 0;
uint64_t accumulated_stat_timer_ticks = 0;
for (int i = 0; i < pairs.size(); i++) {
if (includeZeros || (*counts)[pairs[i].second] > 0)
printf("%s: %ld\n", pairs[i].first.c_str(), (*counts)[pairs[i].second]);
if (includeZeros || (*counts)[pairs[i].second] > 0) {
if (startswith(pairs[i].first, "us_") || startswith(pairs[i].first, "_init_us_")) {
fprintf(stderr, "%s: %lu\n", pairs[i].first.c_str(),
(uint64_t)((*counts)[pairs[i].second] / cycles_per_us));
} else
fprintf(stderr, "%s: %lu\n", pairs[i].first.c_str(), (*counts)[pairs[i].second]);
if (startswith(pairs[i].first, "us_timer_"))
accumulated_stat_timer_ticks += (*counts)[pairs[i].second];
if (pairs[i].first == "ticks_in_main")
ticks_in_main = (*counts)[pairs[i].second];
}
}
if (includeZeros || accumulated_stat_timer_ticks > 0)
fprintf(stderr, "ticks_all_timers: %lu\n", accumulated_stat_timer_ticks);
#if 0
// I want to enable this, but am leaving it disabled for the time
// being because it causes test failures due to:
//
// 1) some tests exit from main from inside catch blocks, without
// going through the logic to stop the timers.
// 2) some tests create multiple threads which causes problems
// with our non-per thread stat timers.
if (ticks_in_main && ticks_in_main != accumulated_stat_timer_ticks) {
fprintf(stderr, "WARNING: accumulated stat timer ticks != ticks in main - don't trust timer output.");
}
#endif
fprintf(stderr, "(End of stats)\n");
}
void Stats::endOfInit() {
......
......@@ -22,23 +22,48 @@
#include <vector>
#include "core/options.h"
#include "core/util.h"
namespace pyston {
#define DISABLE_STATS 0
#define STAT_ALLOCATIONS 0 && !DISABLE_STATS
#define STAT_TIMERS 0 && !DISABLE_STATS
#if STAT_TIMERS
#define STAT_TIMER(id, name) \
static int _stid##id = Stats::getStatId(name); \
StatTimer _st##id(_stid##id)
#define STAT_TIMER2(id, name, at_time) \
static int _stid##id = Stats::getStatId(name); \
StatTimer _st##id(_stid##id, at_time)
#else
#define STAT_TIMER(id, name) StatTimer _st##id(0);
#define STAT_TIMER2(id, name, at_time) StatTimer _st##id(0);
#endif
#define STAT_TIMER_NAME(id) _st##id
#if !DISABLE_STATS
struct Stats {
private:
static std::vector<long>* counts;
static std::vector<uint64_t>* counts;
static std::unordered_map<int, std::string>* names;
static bool enabled;
static timespec start_ts;
static uint64_t start_tick;
public:
static void startEstimatingCPUFreq();
static double estimateCPUFreq();
static int getStatId(const std::string& name);
static std::string getStatName(int id);
static void setEnabled(bool enabled) { Stats::enabled = enabled; }
static void log(int id, int count = 1) { (*counts)[id] += count; }
static void log(int id, uint64_t count = 1) { (*counts)[id] += count; }
static void clear() { std::fill(counts->begin(), counts->end(), 0); }
static void dump(bool includeZeros = true);
......@@ -52,7 +77,7 @@ private:
public:
StatCounter(const std::string& name);
void log(int count = 1) { Stats::log(id, count); }
void log(uint64_t count = 1) { Stats::log(id, count); }
};
struct StatPerThreadCounter {
......@@ -62,24 +87,77 @@ private:
public:
StatPerThreadCounter(const std::string& name);
void log(int count = 1) { Stats::log(id, count); }
void log(uint64_t count = 1) { Stats::log(id, count); }
};
#else
struct Stats {
static void startEstimatingCPUFreq() {}
static double estimateCPUFreq() { return 0; }
static void setEnabled(bool enabled) {}
static void dump() { printf("(Stats disabled)\n"); }
static void dump(bool includeZeros = true) { printf("(Stats disabled)\n"); }
static void clear() {}
static void log(int id, int count = 1) {}
static int getStatId(const std::string& name) { return 0; }
static void endOfInit() {}
};
struct StatCounter {
StatCounter(const char* name) {}
void log(int count = 1){};
void log(uint64_t count = 1){};
};
struct StatPerThreadCounter {
StatPerThreadCounter(const char* name) {}
void log(int count = 1){};
void log(uint64_t count = 1){};
};
#endif
#if STAT_TIMERS
class StatTimer {
private:
static __thread StatTimer* stack;
// the accumulated active duration of this timer
uint64_t _duration;
// the start time of the current active segment (0 == paused)
uint64_t _start_time;
uint64_t _last_pause_time;
StatTimer* _prev;
int _statid;
public:
StatTimer(int statid, bool push = true);
StatTimer(int statid, uint64_t at_time);
~StatTimer();
void pause(uint64_t at_time);
void resume(uint64_t at_time);
bool isPaused() const { return _start_time == 0; }
int getId() const { return _statid; }
static StatTimer* getStack() { return stack; }
static StatTimer* swapStack(StatTimer* s, uint64_t at_time);
static void assertActive() { RELEASE_ASSERT(stack && !stack->isPaused(), ""); }
};
#else
struct StatTimer {
StatTimer(int statid, bool push = true) {}
StatTimer(int statid, uint64_t at_time) {}
~StatTimer() {}
bool isPaused() const { return false; }
void pause(uint64_t at_time) {}
void resume(uint64_t at_time) {}
static StatTimer* getStack() { return NULL; }
static StatTimer* swapStack(StatTimer* s, uint64_t at_time) { return NULL; }
static void assertActive() {}
};
#endif
}
......
......@@ -243,8 +243,6 @@ public:
ScopingAnalysis* scoping;
AST* ast;
CFG* cfg;
LivenessAnalysis* liveness;
std::unordered_map<const OSREntryDescriptor*, PhiAnalysis*> phis;
bool is_generator;
std::string fn; // equivalent of code.co_filename
......@@ -487,13 +485,26 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
return Box::operator new(size, default_cls); \
}
#if STAT_ALLOCATIONS
#define ALLOC_STATS(cls) \
std::string per_name_alloc_name = "alloc." + std::string(cls->tp_name); \
std::string per_name_allocsize_name = "allocsize." + std::string(cls->tp_name); \
Stats::log(Stats::getStatId(per_name_alloc_name)); \
Stats::log(Stats::getStatId(per_name_allocsize_name), size);
#else
#define ALLOC_STATS(cls)
#endif
// The restrictions on when you can use the SIMPLE (ie fast) variant are encoded as
// asserts in the 1-arg operator new function:
#define DEFAULT_CLASS_SIMPLE(default_cls) \
void* operator new(size_t size, BoxedClass * cls) __attribute__((visibility("default"))) { \
ALLOC_STATS(cls); \
return Box::operator new(size, cls); \
} \
void* operator new(size_t size) __attribute__((visibility("default"))) { \
ALLOC_STATS(default_cls); \
/* In the simple cases, we can inline the following methods and simplify things a lot: \
* - Box::operator new \
* - cls->tp_alloc \
......
......@@ -16,6 +16,9 @@
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
......@@ -26,6 +29,18 @@
namespace pyston {
static inline uint64_t rdtsc() {
unsigned long lo, hi;
asm("rdtsc" : "=a"(lo), "=d"(hi));
return (lo | (hi << 32));
}
uint64_t getCPUTicks() {
// unsigned int _unused;
// return __rdtscp(&_unused);
return rdtsc();
}
int Timer::level = 0;
Timer::Timer(const char* desc, long min_usec) : min_usec(min_usec), ended(true) {
......@@ -36,7 +51,7 @@ void Timer::restart(const char* newdesc) {
assert(ended);
desc = newdesc;
gettimeofday(&start_time, NULL);
start_time = getCPUTicks();
Timer::level++;
ended = false;
}
......@@ -46,14 +61,14 @@ void Timer::restart(const char* newdesc, long new_min_usec) {
restart(newdesc);
}
long Timer::end() {
uint64_t Timer::end(uint64_t* ended_at) {
if (!ended) {
timeval end;
gettimeofday(&end, NULL);
long us = 1000000L * (end.tv_sec - start_time.tv_sec) + (end.tv_usec - start_time.tv_usec);
uint64_t end = getCPUTicks();
uint64_t duration = end - start_time;
Timer::level--;
if (VERBOSITY("time") >= 2 && desc) {
uint64_t us = (uint64_t)(duration / Stats::estimateCPUFreq());
if (us > min_usec) {
for (int i = 0; i < Timer::level; i++) {
putchar(' ');
......@@ -70,15 +85,17 @@ long Timer::end() {
fflush(stdout);
}
}
if (ended_at)
*ended_at = end;
ended = true;
return us;
return duration;
}
return -1;
}
Timer::~Timer() {
if (!ended) {
long t = end();
uint64_t t = end();
if (exit_callback)
exit_callback(t);
}
......
......@@ -24,30 +24,36 @@
namespace pyston {
uint64_t getCPUTicks();
class Timer {
private:
static int level;
timeval start_time;
uint64_t start_time;
const char* desc;
long min_usec;
bool ended;
std::function<void(long)> exit_callback;
std::function<void(uint64_t)> exit_callback;
public:
Timer(const char* desc = NULL, long min_usec = -1);
~Timer();
void setExitCallback(std::function<void(long)> _exit_callback) { exit_callback = _exit_callback; }
void setExitCallback(std::function<void(uint64_t)> _exit_callback) { exit_callback = _exit_callback; }
void restart(const char* newdesc, long new_min_usec);
void restart(const char* newdesc = NULL);
long end();
long split(const char* newdesc = NULL) {
long rtn = end();
// returns the duration. if @ended_at is non-null, it's filled in
// with the tick the timer stopped at.
uint64_t end(uint64_t* ended_at = NULL);
uint64_t split(const char* newdesc = NULL) {
uint64_t rtn = end();
restart(newdesc);
return rtn;
}
uint64_t getStartTime() const { return start_time; }
};
bool startswith(const std::string& s, const std::string& pattern);
......
......@@ -339,6 +339,8 @@ void runCollection() {
static StatCounter sc("gc_collections");
sc.log();
STAT_TIMER(t0, "us_timer_gc_collection");
ncollections++;
if (VERBOSITY("gc") >= 2)
......
......@@ -28,7 +28,9 @@ namespace pyston {
namespace gc {
static StatCounter gc_alloc_bytes("zzz_gc_alloc_bytes");
extern "C" inline void* gc_alloc(size_t bytes, GCKind kind_id) {
STAT_TIMER(t0, "us_timer_gc_alloc");
size_t alloc_bytes = bytes + sizeof(GCAllocation);
#ifndef NVALGRIND
......@@ -89,8 +91,10 @@ extern "C" inline void* gc_alloc(size_t bytes, GCKind kind_id) {
// if (VERBOSITY()) printf("Allocated %ld bytes at [%p, %p)\n", bytes, r, (char*)r + bytes);
#endif
// printf("Allocated %p\n", r);
#if STAT_ALLOCATIONS
gc_alloc_bytes.log(bytes);
#endif
return r;
}
......@@ -128,6 +132,10 @@ extern "C" inline void* gc_realloc(void* ptr, size_t bytes) {
alloc->kind_data = bytes;
}
#if STAT_ALLOCATIONS
gc_alloc_bytes.log(bytes);
#endif
return rtn;
}
......
......@@ -185,16 +185,32 @@ struct HeapStatistics {
void print(const char* name) const {
if (nbytes > (1 << 20))
printf("%s: %ld allocations for %.1f MB\n", name, nallocs, nbytes * 1.0 / (1 << 20));
fprintf(stderr, "%s: %ld allocations for %.1f MB\n", name, nallocs, nbytes * 1.0 / (1 << 20));
else if (nbytes > (1 << 10))
printf("%s: %ld allocations for %.1f KB\n", name, nallocs, nbytes * 1.0 / (1 << 10));
fprintf(stderr, "%s: %ld allocations for %.1f KB\n", name, nallocs, nbytes * 1.0 / (1 << 10));
else
printf("%s: %ld allocations for %ld bytes\n", name, nallocs, nbytes);
fprintf(stderr, "%s: %ld allocations for %ld bytes\n", name, nallocs, nbytes);
}
};
bool collect_cls_stats, collect_hcls_stats;
// For use if collect_cls_stats == true:
std::unordered_map<BoxedClass*, TypeStats> by_cls;
TypeStats conservative, untracked, hcls;
// For use if collect_hcls_stats == true:
std::unordered_map<HiddenClass*, int> hcls_uses;
#define HCLS_ATTRS_STAT_MAX 20
int num_hcls_by_attrs[HCLS_ATTRS_STAT_MAX + 1];
int num_hcls_by_attrs_exceed;
TypeStats python, conservative, untracked, hcls, precise;
TypeStats total;
HeapStatistics(bool collect_cls_stats, bool collect_hcls_stats)
: collect_cls_stats(collect_cls_stats), collect_hcls_stats(collect_hcls_stats) {
memset(num_hcls_by_attrs, 0, sizeof(num_hcls_by_attrs));
}
};
void addStatistic(HeapStatistics* stats, GCAllocation* al, int nbytes) {
......@@ -202,11 +218,24 @@ void addStatistic(HeapStatistics* stats, GCAllocation* al, int nbytes) {
stats->total.nbytes += nbytes;
if (al->kind_id == GCKind::PYTHON) {
Box* b = (Box*)al->user_data;
auto& t = stats->by_cls[b->cls];
stats->python.nallocs++;
stats->python.nbytes += nbytes;
t.nallocs++;
t.nbytes += nbytes;
if (stats->collect_cls_stats) {
Box* b = (Box*)al->user_data;
auto& t = stats->by_cls[b->cls];
t.nallocs++;
t.nbytes += nbytes;
}
if (stats->collect_hcls_stats) {
Box* b = (Box*)al->user_data;
if (b->cls->instancesHaveHCAttrs()) {
HCAttrs* attrs = b->getHCAttrsPtr();
stats->hcls_uses[attrs->hcls]++;
}
}
} else if (al->kind_id == GCKind::CONSERVATIVE) {
stats->conservative.nallocs++;
stats->conservative.nbytes += nbytes;
......@@ -216,6 +245,18 @@ void addStatistic(HeapStatistics* stats, GCAllocation* al, int nbytes) {
} else if (al->kind_id == GCKind::HIDDEN_CLASS) {
stats->hcls.nallocs++;
stats->hcls.nbytes += nbytes;
if (stats->collect_hcls_stats) {
HiddenClass* hcls = (HiddenClass*)al->user_data;
int numattrs = hcls->attributeArraySize();
if (numattrs <= HCLS_ATTRS_STAT_MAX)
stats->num_hcls_by_attrs[numattrs]++;
else
stats->num_hcls_by_attrs_exceed++;
}
} else if (al->kind_id == GCKind::PRECISE) {
stats->precise.nallocs++;
stats->precise.nbytes += nbytes;
} else {
RELEASE_ASSERT(0, "%d", (int)al->kind_id);
}
......@@ -223,27 +264,49 @@ void addStatistic(HeapStatistics* stats, GCAllocation* al, int nbytes) {
void Heap::dumpHeapStatistics() {
void Heap::dumpHeapStatistics(int level) {
bool collect_cls_stats = (level >= 1);
bool collect_hcls_stats = (level >= 1);
threading::GLPromoteRegion _lock;
HeapStatistics stats;
fprintf(stderr, "\nCollecting heap stats for pid %d...\n", getpid());
HeapStatistics stats(collect_cls_stats, collect_hcls_stats);
small_arena.getStatistics(&stats);
large_arena.getStatistics(&stats);
huge_arena.getStatistics(&stats);
stats.python.print("python");
stats.conservative.print("conservative");
stats.untracked.print("untracked");
stats.hcls.print("hcls");
for (const auto& p : stats.by_cls) {
p.second.print(getFullNameOfClass(p.first).c_str());
stats.precise.print("precise");
if (collect_cls_stats) {
for (const auto& p : stats.by_cls) {
p.second.print(getFullNameOfClass(p.first).c_str());
}
}
stats.total.print("Total");
printf("\n");
if (collect_hcls_stats) {
fprintf(stderr, "%ld hidden classes currently alive\n", stats.hcls.nallocs);
fprintf(stderr, "%ld have at least one Box that uses them\n", stats.hcls_uses.size());
for (int i = 0; i <= HCLS_ATTRS_STAT_MAX; i++) {
fprintf(stderr, "With % 3d attributes: %d\n", i, stats.num_hcls_by_attrs[i]);
}
fprintf(stderr, "With >% 2d attributes: %d\n", HCLS_ATTRS_STAT_MAX, stats.num_hcls_by_attrs_exceed);
}
fprintf(stderr, "\n");
}
void dumpHeapStatistics() {
global_heap.dumpHeapStatistics();
void dumpHeapStatistics(int level) {
global_heap.dumpHeapStatistics(level);
}
//////
......
......@@ -22,6 +22,7 @@
#include "core/common.h"
#include "core/threading.h"
#include "core/types.h"
namespace pyston {
......@@ -535,13 +536,13 @@ public:
huge_arena.freeUnmarked(weakly_referenced);
}
void dumpHeapStatistics();
void dumpHeapStatistics(int level);
friend void markPhase();
};
extern Heap global_heap;
void dumpHeapStatistics();
void dumpHeapStatistics(int level);
} // namespace gc
} // namespace pyston
......
This diff is collapsed.
......@@ -304,6 +304,7 @@ extern "C" Box* ord(Box* obj) {
}
Box* range(Box* start, Box* stop, Box* step) {
STAT_TIMER(t0, "us_timer_builtin_range");
i64 istart, istop, istep;
if (stop == NULL) {
istart = 0;
......@@ -347,6 +348,7 @@ Box* notimplementedRepr(Box* self) {
}
Box* sorted(Box* obj, Box* cmp, Box* key, Box** args) {
STAT_TIMER(t0, "us_timer_builtin_sorted");
Box* reverse = args[0];
BoxedList* rtn = new BoxedList();
......@@ -359,6 +361,7 @@ Box* sorted(Box* obj, Box* cmp, Box* key, Box** args) {
}
Box* isinstance_func(Box* obj, Box* cls) {
STAT_TIMER(t0, "us_timer_builtin_isinstance");
int rtn = PyObject_IsInstance(obj, cls);
if (rtn < 0)
checkAndThrowCAPIException();
......@@ -366,6 +369,7 @@ Box* isinstance_func(Box* obj, Box* cls) {
}
Box* issubclass_func(Box* child, Box* parent) {
STAT_TIMER(t0, "us_timer_builtin_issubclass");
int rtn = PyObject_IsSubclass(child, parent);
if (rtn < 0)
checkAndThrowCAPIException();
......
......@@ -19,7 +19,7 @@
namespace pyston {
static Box* memberGet(BoxedMemberDescriptor* self, Box* inst, Box* owner) {
RELEASE_ASSERT(self->cls == member_cls, "");
RELEASE_ASSERT(self->cls == member_descriptor_cls, "");
if (inst == None)
return self;
......@@ -169,8 +169,8 @@ static Box* classmethodGet(Box* self, Box* obj, Box* type) {
}
void setupDescr() {
member_cls->giveAttr("__get__", new BoxedFunction(boxRTFunction((void*)memberGet, UNKNOWN, 3)));
member_cls->freeze();
member_descriptor_cls->giveAttr("__get__", new BoxedFunction(boxRTFunction((void*)memberGet, UNKNOWN, 3)));
member_descriptor_cls->freeze();
property_cls->giveAttr("__init__",
new BoxedFunction(boxRTFunction((void*)propertyInit, UNKNOWN, 5, 4, false, false,
......
......@@ -48,6 +48,7 @@ Box* dictRepr(BoxedDict* self) {
}
Box* dictClear(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictClear");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'clear' requires a 'dict' object but received a '%s'", getTypeName(self));
......@@ -56,6 +57,7 @@ Box* dictClear(BoxedDict* self) {
}
Box* dictCopy(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictCopy");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'copy' requires a 'dict' object but received a '%s'", getTypeName(self));
......@@ -65,6 +67,7 @@ Box* dictCopy(BoxedDict* self) {
}
Box* dictItems(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictItems");
BoxedList* rtn = new BoxedList();
rtn->ensure(self->d.size());
......@@ -77,6 +80,7 @@ Box* dictItems(BoxedDict* self) {
}
Box* dictValues(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictValues");
BoxedList* rtn = new BoxedList();
rtn->ensure(self->d.size());
for (const auto& p : self->d) {
......@@ -86,6 +90,7 @@ Box* dictValues(BoxedDict* self) {
}
Box* dictKeys(BoxedDict* self) {
STAT_TIMER(t0, "us_timer_dictKeys");
RELEASE_ASSERT(isSubclass(self->cls, dict_cls), "");
BoxedList* rtn = new BoxedList();
......@@ -182,6 +187,7 @@ extern "C" int PyDict_Update(PyObject* a, PyObject* b) noexcept {
}
Box* dictGetitem(BoxedDict* self, Box* k) {
STAT_TIMER(t0, "us_timer_dictGetitem");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor '__getitem__' requires a 'dict' object but received a '%s'",
getTypeName(self));
......@@ -306,6 +312,7 @@ extern "C" PyObject* PyDict_GetItemString(PyObject* dict, const char* key) noexc
}
Box* dictSetitem(BoxedDict* self, Box* k, Box* v) {
STAT_TIMER(t0, "us_timer_dictSetitem");
// printf("Starting setitem\n");
Box*& pos = self->d[k];
// printf("Got the pos\n");
......@@ -320,6 +327,7 @@ Box* dictSetitem(BoxedDict* self, Box* k, Box* v) {
}
Box* dictDelitem(BoxedDict* self, Box* k) {
STAT_TIMER(t0, "us_timer_dictDelitem");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor '__delitem__' requires a 'dict' object but received a '%s'",
getTypeName(self));
......
......@@ -741,6 +741,98 @@ static Box* float0(Box*, void*) {
return boxFloat(0.0);
}
// __getformat__
// ported pretty directly from cpython Objects/floatobject.c
typedef enum { unknown_format, ieee_big_endian_format, ieee_little_endian_format } float_format_type;
static float_format_type double_format, float_format;
static float_format_type detected_double_format, detected_float_format;
static void floatFormatInit() {
/* We attempt to determine if this machine is using IEEE
floating point formats by peering at the bits of some
carefully chosen values. If it looks like we are on an
IEEE platform, the float packing/unpacking routines can
just copy bits, if not they resort to arithmetic & shifts
and masks. The shifts & masks approach works on all finite
values, but what happens to infinities, NaNs and signed
zeroes on packing is an accident, and attempting to unpack
a NaN or an infinity will raise an exception.
Note that if we're on some whacked-out platform which uses
IEEE formats but isn't strictly little-endian or big-
endian, we will fall back to the portable shifts & masks
method. */
if (sizeof(double) == 8) {
double x = 9006104071832581.0;
if (memcmp(&x, "\x43\x3f\xff\x01\x02\x03\x04\x05", 8) == 0)
detected_double_format = ieee_big_endian_format;
else if (memcmp(&x, "\x05\x04\x03\x02\x01\xff\x3f\x43", 8) == 0)
detected_double_format = ieee_little_endian_format;
else
detected_double_format = unknown_format;
} else {
detected_double_format = unknown_format;
}
if (sizeof(float) == 4) {
float y = 16711938.0;
if (memcmp(&y, "\x4b\x7f\x01\x02", 4) == 0)
detected_float_format = ieee_big_endian_format;
else if (memcmp(&y, "\x02\x01\x7f\x4b", 4) == 0)
detected_float_format = ieee_little_endian_format;
else
detected_float_format = unknown_format;
} else {
detected_float_format = unknown_format;
}
double_format = detected_double_format;
float_format = detected_float_format;
}
// ported pretty directly from cpython
Box* floatGetFormat(BoxedClass* v, Box* arg) {
char* s;
float_format_type r;
if (!PyString_Check(arg)) {
raiseExcHelper(TypeError, "__getformat__() argument must be string, not %s", arg->cls->tp_name);
}
s = PyString_AS_STRING(arg);
if (strcmp(s, "double") == 0) {
r = double_format;
} else if (strcmp(s, "float") == 0) {
r = float_format;
} else {
raiseExcHelper(ValueError, "__getformat__() argument 1 must be "
"'double' or 'float'");
}
switch (r) {
case unknown_format:
return boxString("unknown");
case ieee_little_endian_format:
return boxString("IEEE, little-endian");
case ieee_big_endian_format:
return boxString("IEEE, big-endian");
default:
RELEASE_ASSERT(false, "insane float_format or double_format");
return NULL;
}
}
const char* floatGetFormatDoc = "float.__getformat__(typestr) -> string\n"
"\n"
"You probably don't want to use this function. It exists mainly to be\n"
"used in Python's test suite.\n"
"\n"
"typestr must be 'double' or 'float'. This function returns whichever of\n"
"'unknown', 'IEEE, big-endian' or 'IEEE, little-endian' best describes the\n"
"format of floating point numbers used by the C type named by typestr.";
void setupFloat() {
_addFunc("__add__", BOXED_FLOAT, (void*)floatAddFloat, (void*)floatAddInt, (void*)floatAdd);
float_cls->giveAttr("__radd__", float_cls->getattr("__add__"));
......@@ -785,7 +877,13 @@ void setupFloat() {
float_cls->giveAttr("imag", new (pyston_getset_cls) BoxedGetsetDescriptor(float0, NULL, NULL));
float_cls->giveAttr("conjugate", new BoxedFunction(boxRTFunction((void*)floatFloat, BOXED_FLOAT, 1)));
float_cls->giveAttr("__getformat__",
new BoxedClassmethod(new BoxedBuiltinFunctionOrMethod(
boxRTFunction((void*)floatGetFormat, STR, 2), "__getformat__", floatGetFormatDoc)));
float_cls->freeze();
floatFormatInit();
}
void teardownFloat() {
......
......@@ -86,28 +86,36 @@ Context* getReturnContextForGeneratorFrame(void* frame_addr) {
}
void generatorEntry(BoxedGenerator* g) {
assert(g->cls == generator_cls);
assert(g->function->cls == function_cls);
{
STAT_TIMER2(t0, "us_timer_generator_toplevel", g->timer_time);
threading::pushGenerator(g, g->stack_begin, g->returnContext);
assert(g->cls == generator_cls);
assert(g->function->cls == function_cls);
try {
RegisterHelper context_registerer(g, __builtin_frame_address(0));
threading::pushGenerator(g, g->stack_begin, g->returnContext);
try {
RegisterHelper context_registerer(g, __builtin_frame_address(0));
// call body of the generator
BoxedFunctionBase* func = g->function;
// call body of the generator
BoxedFunctionBase* func = g->function;
Box** args = g->args ? &g->args->elts[0] : nullptr;
callCLFunc(func->f, nullptr, func->f->numReceivedArgs(), func->closure, g, func->globals, g->arg1, g->arg2,
g->arg3, args);
} catch (ExcInfo e) {
// unhandled exception: propagate the exception to the caller
g->exception = e;
}
Box** args = g->args ? &g->args->elts[0] : nullptr;
callCLFunc(func->f, nullptr, func->f->numReceivedArgs(), func->closure, g, func->globals, g->arg1, g->arg2,
g->arg3, args);
} catch (ExcInfo e) {
// unhandled exception: propagate the exception to the caller
g->exception = e;
}
// we returned from the body of the generator. next/send/throw will notify the caller
g->entryExited = true;
threading::popGenerator();
// we returned from the body of the generator. next/send/throw will notify the caller
g->entryExited = true;
threading::popGenerator();
#if STAT_TIMERS
g->timer_time = getCPUTicks(); // store off the timer that our caller (in g->returnContext) will resume at
STAT_TIMER_NAME(t0).pause(g->timer_time);
#endif
}
swapContext(&g->context, g->returnContext, 0);
}
......@@ -130,7 +138,21 @@ Box* generatorSend(Box* s, Box* v) {
self->returnValue = v;
self->running = true;
#if STAT_TIMERS
// store off the time that the generator will use to initialize its toplevel timer
self->timer_time = getCPUTicks();
StatTimer* current_timers = StatTimer::swapStack(self->statTimers, self->timer_time);
#endif
swapContext(&self->returnContext, self->context, (intptr_t)self);
#if STAT_TIMERS
// if the generator exited we use the time that generatorEntry stored in self->timer_time (the same time it paused
// its timer at).
self->statTimers = StatTimer::swapStack(current_timers, self->entryExited ? self->timer_time : getCPUTicks());
#endif
self->running = false;
// propagate exception to the caller
......
......@@ -541,6 +541,8 @@ extern "C" Box* listInsert(BoxedList* self, Box* idx, Box* v) {
}
Box* listMul(BoxedList* self, Box* rhs) {
STAT_TIMER(t0, "us_timer_listMul");
if (rhs->cls != int_cls) {
raiseExcHelper(TypeError, "can't multiply sequence by non-int of type '%s'", getTypeName(rhs));
}
......@@ -940,7 +942,7 @@ Box* _listCmp(BoxedList* lhs, BoxedList* rhs, AST_TYPE::AST_TYPE op_type) {
}
Box* listEq(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......@@ -950,7 +952,7 @@ Box* listEq(BoxedList* self, Box* rhs) {
}
Box* listNe(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......@@ -960,7 +962,7 @@ Box* listNe(BoxedList* self, Box* rhs) {
}
Box* listLt(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......@@ -970,7 +972,7 @@ Box* listLt(BoxedList* self, Box* rhs) {
}
Box* listLe(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......@@ -980,7 +982,7 @@ Box* listLe(BoxedList* self, Box* rhs) {
}
Box* listGt(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......@@ -990,7 +992,7 @@ Box* listGt(BoxedList* self, Box* rhs) {
}
Box* listGe(BoxedList* self, Box* rhs) {
if (rhs->cls != list_cls) {
if (!isSubclass(rhs->cls, list_cls)) {
return NotImplemented;
}
......
This diff is collapsed.
......@@ -96,6 +96,7 @@ void unwindExc(Box* exc_obj) {
void raiseRaw(const ExcInfo& e) __attribute__((__noreturn__));
void raiseRaw(const ExcInfo& e) {
STAT_TIMER(t0, "us_timer_raiseraw");
// Should set these to None before getting here:
assert(e.type);
assert(e.value);
......@@ -160,28 +161,31 @@ extern "C" void abort() {
// In case displaying the traceback recursively calls abort:
static bool recursive = false;
// If traceback_cls is NULL, then we somehow died early on, and won't be able to display a traceback.
if (!recursive && traceback_cls) {
if (!recursive) {
recursive = true;
Stats::dump();
fprintf(stderr, "Someone called abort!\n");
// If we call abort(), things may be seriously wrong. Set an alarm() to
// try to handle cases that we would just hang.
// (Ex if we abort() from a static constructor, and _printStackTrace uses
// that object, _printStackTrace will hang waiting for the first construction
// to finish.)
alarm(1);
try {
_printStacktrace();
} catch (ExcInfo) {
fprintf(stderr, "error printing stack trace during abort()");
}
// If traceback_cls is NULL, then we somehow died early on, and won't be able to display a traceback.
if (traceback_cls) {
// If we call abort(), things may be seriously wrong. Set an alarm() to
// try to handle cases that we would just hang.
// (Ex if we abort() from a static constructor, and _printStackTrace uses
// that object, _printStackTrace will hang waiting for the first construction
// to finish.)
alarm(1);
try {
_printStacktrace();
} catch (ExcInfo) {
fprintf(stderr, "error printing stack trace during abort()");
}
// Cancel the alarm.
// This is helpful for when running in a debugger, since otherwise the debugger will catch the
// abort and let you investigate, but the alarm will still come back to kill the program.
alarm(0);
// Cancel the alarm.
// This is helpful for when running in a debugger, since otherwise the debugger will catch the
// abort and let you investigate, but the alarm will still come back to kill the program.
alarm(0);
}
}
if (PAUSE_AT_ABORT) {
......
......@@ -1112,6 +1112,7 @@ extern "C" Box* strMod(BoxedString* lhs, Box* rhs) {
}
extern "C" Box* strMul(BoxedString* lhs, Box* rhs) {
STAT_TIMER(t0, "us_timer_strMul");
assert(isSubclass(lhs->cls, str_cls));
int n;
......@@ -1514,6 +1515,7 @@ failed:
}
extern "C" size_t unicodeHashUnboxed(PyUnicodeObject* self) {
STAT_TIMER(t0, "us_timer_unicodeHashUnboxed");
if (self->hash != -1)
return self->hash;
......@@ -1524,6 +1526,7 @@ extern "C" size_t unicodeHashUnboxed(PyUnicodeObject* self) {
}
extern "C" Box* strHash(BoxedString* self) {
STAT_TIMER(t0, "us_timer_strHash");
assert(isSubclass(self->cls, str_cls));
StringHash<char> H;
......
......@@ -168,6 +168,7 @@ Box* tupleAdd(BoxedTuple* self, Box* rhs) {
}
Box* tupleMul(BoxedTuple* self, Box* rhs) {
STAT_TIMER(t0, "us_timer_tupleMul");
if (rhs->cls != int_cls) {
raiseExcHelper(TypeError, "can't multiply sequence by non-int of type '%s'", getTypeName(rhs));
}
......@@ -337,6 +338,7 @@ Box* tupleIndex(BoxedTuple* self, Box* elt) {
}
Box* tupleHash(BoxedTuple* self) {
STAT_TIMER(t0, "us_timer_tupleHash");
assert(isSubclass(self->cls, tuple_cls));
int64_t rtn = 3527539;
......
......@@ -655,7 +655,7 @@ extern "C" void closureGCHandler(GCVisitor* v, Box* b) {
extern "C" {
BoxedClass* object_cls, *type_cls, *none_cls, *bool_cls, *int_cls, *float_cls,
* str_cls = NULL, *function_cls, *instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls,
*file_cls, *member_cls, *closure_cls, *generator_cls, *complex_cls, *basestring_cls, *property_cls,
*file_cls, *member_descriptor_cls, *closure_cls, *generator_cls, *complex_cls, *basestring_cls, *property_cls,
*staticmethod_cls, *classmethod_cls, *attrwrapper_cls, *pyston_getset_cls, *capi_getset_cls,
*builtin_function_or_method_cls, *attrwrapperiter_cls, *set_cls, *frozenset_cls;
......@@ -1270,6 +1270,8 @@ public:
}
static Box* setitem(Box* _self, Box* _key, Box* value) {
STAT_TIMER(t0, "us_timer_AttrWrapper_setitem");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1282,6 +1284,7 @@ public:
}
static Box* setdefault(Box* _self, Box* _key, Box* value) {
STAT_TIMER(t0, "us_timer_AttrWrapper_setdefault");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1297,6 +1300,7 @@ public:
}
static Box* get(Box* _self, Box* _key, Box* def) {
STAT_TIMER(t0, "us_timer_AttrWrapper_get");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1311,6 +1315,7 @@ public:
}
static Box* getitem(Box* _self, Box* _key) {
STAT_TIMER(t0, "us_timer_AttrWrapper_getitem");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1344,6 +1349,7 @@ public:
}
static Box* delitem(Box* _self, Box* _key) {
STAT_TIMER(t0, "us_timer_AttrWrapper_delitem");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1409,6 +1415,7 @@ public:
}
static Box* values(Box* _self) {
STAT_TIMER(t0, "us_timer_AttrWrapper_values");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1423,6 +1430,7 @@ public:
}
static Box* items(Box* _self) {
STAT_TIMER(t0, "us_timer_AttrWrapper_items");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1461,6 +1469,7 @@ public:
}
static Box* update(Box* _self, Box* _container) {
STAT_TIMER(t0, "us_timer_AttrWrapper_update");
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -2101,8 +2110,8 @@ void setupRuntime() {
module_cls = new BoxedHeapClass(object_cls, &moduleGCHandler, offsetof(BoxedModule, attrs), 0, sizeof(BoxedModule),
false, static_cast<BoxedString*>(boxStrConstant("module")));
member_cls = new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedMemberDescriptor), false,
static_cast<BoxedString*>(boxStrConstant("member")));
member_descriptor_cls = new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedMemberDescriptor), false,
static_cast<BoxedString*>(boxStrConstant("member_descriptor")));
capifunc_cls = new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedCApiFunction), false,
static_cast<BoxedString*>(boxStrConstant("capifunc")));
method_cls = new BoxedHeapClass(object_cls, NULL, 0, 0, sizeof(BoxedMethodDescriptor), false,
......@@ -2132,7 +2141,7 @@ void setupRuntime() {
float_cls->tp_mro = BoxedTuple::create({ float_cls, object_cls });
function_cls->tp_mro = BoxedTuple::create({ function_cls, object_cls });
builtin_function_or_method_cls->tp_mro = BoxedTuple::create({ builtin_function_or_method_cls, object_cls });
member_cls->tp_mro = BoxedTuple::create({ member_cls, object_cls });
member_descriptor_cls->tp_mro = BoxedTuple::create({ member_descriptor_cls, object_cls });
capifunc_cls->tp_mro = BoxedTuple::create({ capifunc_cls, object_cls });
module_cls->tp_mro = BoxedTuple::create({ module_cls, object_cls });
method_cls->tp_mro = BoxedTuple::create({ method_cls, object_cls });
......@@ -2187,7 +2196,7 @@ void setupRuntime() {
float_cls->finishInitialization();
function_cls->finishInitialization();
builtin_function_or_method_cls->finishInitialization();
member_cls->finishInitialization();
member_descriptor_cls->finishInitialization();
module_cls->finishInitialization();
capifunc_cls->finishInitialization();
method_cls->finishInitialization();
......
......@@ -85,9 +85,9 @@ extern "C" Box* getSysStdout();
extern "C" {
extern BoxedClass* object_cls, *type_cls, *bool_cls, *int_cls, *long_cls, *float_cls, *str_cls, *function_cls,
*none_cls, *instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *file_cls,
*enumerate_cls, *xrange_cls, *member_cls, *method_cls, *closure_cls, *generator_cls, *complex_cls, *basestring_cls,
*property_cls, *staticmethod_cls, *classmethod_cls, *attrwrapper_cls, *pyston_getset_cls, *capi_getset_cls,
*builtin_function_or_method_cls, *set_cls, *frozenset_cls, *code_cls;
*enumerate_cls, *xrange_cls, *member_descriptor_cls, *method_cls, *closure_cls, *generator_cls, *complex_cls,
*basestring_cls, *property_cls, *staticmethod_cls, *classmethod_cls, *attrwrapper_cls, *pyston_getset_cls,
*capi_getset_cls, *builtin_function_or_method_cls, *set_cls, *frozenset_cls, *code_cls;
}
#define unicode_cls (&PyUnicode_Type)
#define memoryview_cls (&PyMemoryView_Type)
......@@ -410,12 +410,37 @@ public:
size_t size() { return s.size(); }
void* operator new(size_t size, size_t ssize) __attribute__((visibility("default"))) {
#if STAT_ALLOCATIONS
static StatCounter alloc_str("alloc.str");
static StatCounter alloc_str1("alloc.str(1)");
static StatCounter allocsize_str("allocsize.str");
if (ssize == 1)
alloc_str1.log();
else
alloc_str.log();
allocsize_str.log(str_cls->tp_basicsize + ssize + 1);
#endif
Box* rtn = static_cast<Box*>(gc_alloc(str_cls->tp_basicsize + ssize + 1, gc::GCKind::PYTHON));
rtn->cls = str_cls;
return rtn;
}
void* operator new(size_t size, BoxedClass* cls, size_t ssize) __attribute__((visibility("default"))) {
#if STAT_ALLOCATIONS
static StatCounter alloc_str("alloc.str");
static StatCounter alloc_str1("alloc.str(1)");
static StatCounter allocsize_str("allocsize.str");
if (ssize == 1)
alloc_str1.log();
else
alloc_str.log();
allocsize_str.log(cls->tp_basicsize + ssize + 1);
#endif
Box* rtn = static_cast<Box*>(cls->tp_alloc(cls, ssize + 1));
rtn->cls = cls;
return rtn;
......@@ -517,12 +542,43 @@ public:
Box** elts;
void* operator new(size_t size, size_t nelts) __attribute__((visibility("default"))) {
#if STAT_ALLOCATIONS
static StatCounter alloc_tuple("alloc.tuple");
static StatCounter alloc_tuple0("alloc.tuple(0)");
static StatCounter allocsize_tuple("allocsize.tuple");
static StatCounter allocsize_tuple0("allocsize.tuple(0)");
if (nelts == 0) {
alloc_tuple0.log();
allocsize_tuple0.log(_PyObject_VAR_SIZE(tuple_cls, nelts + 1));
} else {
alloc_tuple.log();
allocsize_tuple.log(_PyObject_VAR_SIZE(tuple_cls, nelts + 1));
}
#endif
Box* rtn = static_cast<Box*>(gc_alloc(_PyObject_VAR_SIZE(tuple_cls, nelts + 1), gc::GCKind::PYTHON));
rtn->cls = tuple_cls;
return rtn;
}
void* operator new(size_t size, BoxedClass* cls, size_t nelts) __attribute__((visibility("default"))) {
#if STAT_ALLOCATIONS
static StatCounter alloc_tuple("alloc.tuple");
static StatCounter alloc_tuple0("alloc.tuple(0)");
static StatCounter allocsize_tuple("allocsize.tuple");
static StatCounter allocsize_tuple0("allocsize.tuple(0)");
if (nelts == 0) {
alloc_tuple0.log();
allocsize_tuple0.log(_PyObject_VAR_SIZE(cls, nelts + 1));
} else {
alloc_tuple.log();
allocsize_tuple.log(_PyObject_VAR_SIZE(cls, nelts + 1));
}
#endif
Box* rtn = static_cast<Box*>(cls->tp_alloc(cls, nelts));
rtn->cls = cls;
return rtn;
......@@ -732,7 +788,7 @@ public:
BoxedMemberDescriptor(PyMemberDef* member)
: type((MemberType)member->type), offset(member->offset), readonly(member->flags & READONLY) {}
DEFAULT_CLASS_SIMPLE(member_cls);
DEFAULT_CLASS_SIMPLE(member_descriptor_cls);
};
class BoxedGetsetDescriptor : public Box {
......@@ -817,6 +873,11 @@ public:
struct Context* context, *returnContext;
void* stack_begin;
#if STAT_TIMERS
StatTimer* statTimers;
uint64_t timer_time;
#endif
BoxedGenerator(BoxedFunctionBase* function, Box* arg1, Box* arg2, Box* arg3, Box** args);
DEFAULT_CLASS(generator_cls);
......
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StandardError
| +-- BufferError
| +-- ArithmeticError
| | +-- FloatingPointError
| | +-- OverflowError
| | +-- ZeroDivisionError
| +-- AssertionError
| +-- AttributeError
| +-- EnvironmentError
| | +-- IOError
| | +-- OSError
| | +-- WindowsError (Windows)
| | +-- VMSError (VMS)
| +-- EOFError
| +-- ImportError
| +-- LookupError
| | +-- IndexError
| | +-- KeyError
| +-- MemoryError
| +-- NameError
| | +-- UnboundLocalError
| +-- ReferenceError
| +-- RuntimeError
| | +-- NotImplementedError
| +-- SyntaxError
| | +-- IndentationError
| | +-- TabError
| +-- SystemError
| +-- TypeError
| +-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
s = """
def f():
a = 1
......@@ -11,7 +10,8 @@ f()
try:
exec s
except Exception as e:
print repr(e)
# avoid microrevision changes to Python error messages
print repr(e).replace("because ", "")
s = """
def f():
......@@ -27,4 +27,5 @@ f()
try:
exec s
except Exception as e:
print repr(e)
# avoid microrevision changes to Python error messages
print repr(e).replace("because ", "")
......@@ -50,3 +50,11 @@ except ValueError as e:
pass
# We don't print the right thing yet:
# print e
print '__getformat__ test'
print float.__getformat__('double')
print float.__getformat__('float')
try:
float.__getformat__('oooga booga boooga')
except Exception as e:
print e.message
......@@ -12,3 +12,27 @@ print l
print len(MyList.__new__(MyList))
l[:] = l[:]
print l
print [1,2,3] == MyList((1,2,3,4))
print [1,2,3] != MyList((1,2,3,4))
print [1,2,3,4] > MyList((1,2,3))
print [1,2,3,4] < MyList((1,2,3))
print [1,2,3] > MyList((1,2,3,4))
print [1,2,3] < MyList((1,2,3,4))
print [1,2,3] >= MyList((1,2,3))
print [1,2,3] <= MyList((1,2,3))
print MyList((1,2,3)) == MyList((1,2,3,4))
print MyList((1,2,3)) != MyList((1,2,3,4))
print MyList((1,2,3,4)) > MyList((1,2,3))
print MyList((1,2,3,4)) < MyList((1,2,3))
print MyList((1,2,3)) > MyList((1,2,3,4))
print MyList((1,2,3)) < MyList((1,2,3,4))
print MyList((1,2,3)) >= MyList((1,2,3))
print MyList((1,2,3)) <= MyList((1,2,3))
......@@ -208,6 +208,6 @@ for case in cases:
try:
exec case
except SyntaxError as se:
print se.message
print se.message.replace("because ", "")
# TODO uncomment this
# traceback.print_exc()
import os
import test_package
print 1, test_package.__name__, os.path.normpath(test_package.__file__)
print 1, test_package.__name__, os.path.normpath(test_package.__file__).replace(".pyc", ".py")
import test_package.intrapackage_import
import test_package.absolute_import
......
import os
import import_target
print 3, import_target.__name__, os.path.normpath(import_target.__file__)
print 3, import_target.__name__, os.path.normpath(import_target.__file__).replace(".pyc", ".py")
......@@ -2,4 +2,4 @@ from __future__ import absolute_import
import os
import import_target
print 4, import_target.__name__, os.path.normpath(import_target.__file__)
print 4, import_target.__name__, os.path.normpath(import_target.__file__).replace(".pyc", ".py")
import os
from . import import_target
print 2, import_target.__name__, os.path.normpath(import_target.__file__)
print 2, import_target.__name__, os.path.normpath(import_target.__file__).replace(".pyc", ".py")
......@@ -39,7 +39,7 @@ TEST_F(AnalysisTest, augassign) {
SourceInfo* si = new SourceInfo(createModule("augassign", fn), scoping, func, func->body, fn);
CFG* cfg = computeCFG(si, func->body);
LivenessAnalysis* liveness = computeLivenessInfo(cfg);
std::unique_ptr<LivenessAnalysis> liveness = computeLivenessInfo(cfg);
//cfg->print();
......@@ -49,7 +49,7 @@ TEST_F(AnalysisTest, augassign) {
ASSERT_TRUE(liveness->isLiveAtEnd(module->interned_strings->get("a"), block));
}
PhiAnalysis* phis = computeRequiredPhis(ParamNames(func), cfg, liveness, scope_info);
std::unique_ptr<PhiAnalysis> phis = computeRequiredPhis(ParamNames(func), cfg, liveness.get(), scope_info);
}
void doOsrTest(bool is_osr, bool i_maybe_undefined) {
......@@ -67,7 +67,7 @@ void doOsrTest(bool is_osr, bool i_maybe_undefined) {
scoping, func, func->body, fn);
CFG* cfg = computeCFG(si, func->body);
LivenessAnalysis* liveness = computeLivenessInfo(cfg);
std::unique_ptr<LivenessAnalysis> liveness = computeLivenessInfo(cfg);
// cfg->print();
......@@ -83,7 +83,7 @@ void doOsrTest(bool is_osr, bool i_maybe_undefined) {
AST_Jump* backedge = ast_cast<AST_Jump>(loop_backedge->body[0]);
ASSERT_LE(backedge->target->idx, loop_backedge->idx);
PhiAnalysis* phis;
std::unique_ptr<PhiAnalysis> phis;
if (is_osr) {
OSREntryDescriptor* entry_descriptor = OSREntryDescriptor::create(NULL, backedge);
......@@ -91,9 +91,9 @@ void doOsrTest(bool is_osr, bool i_maybe_undefined) {
if (i_maybe_undefined)
entry_descriptor->args[idi_str] = NULL;
entry_descriptor->args[iter_str] = NULL;
phis = computeRequiredPhis(entry_descriptor, liveness, scope_info);
phis = computeRequiredPhis(entry_descriptor, liveness.get(), scope_info);
} else {
phis = computeRequiredPhis(ParamNames(func), cfg, liveness, scope_info);
phis = computeRequiredPhis(ParamNames(func), cfg, liveness.get(), scope_info);
}
// First, verify that we require phi nodes for the block we enter into.
......
......@@ -125,6 +125,8 @@ def canonicalize_stderr(stderr):
("AttributeError: '(\w+)' object attribute '(\w+)' is read-only", "AttributeError: \\2"),
(r"TypeError: object.__new__\(\) takes no parameters", "TypeError: object() takes no parameters"),
("IndexError: list assignment index out of range", "IndexError: list index out of range"),
(r"unqualified exec is not allowed in function '(\w+)' it (.*)",
r"unqualified exec is not allowed in function '\1' because it \2"),
]
for pattern, subst_with in substitutions:
......@@ -206,9 +208,6 @@ def get_test_options(fn, check_stats, run_memcheck):
elif os.path.basename(fn).split('.')[0] in TESTS_TO_SKIP:
opts.skip = 'command line option'
if opts.collect_stats:
opts.jit_args = ['-s'] + opts.jit_args
assert opts.expected in ("success", "fail", "statfail"), opts.expected
if TEST_PYPY:
......@@ -217,11 +216,12 @@ def get_test_options(fn, check_stats, run_memcheck):
opts.check_stats = False
opts.expected = "success"
if opts.collect_stats:
opts.jit_args = ['-s'] + opts.jit_args
return opts
def determine_test_result(fn, opts, code, out, stderr, elapsed):
last_stderr_line = stderr.strip().split('\n')[-1]
if opts.allow_warnings:
out_lines = []
for l in out.split('\n'):
......@@ -233,13 +233,26 @@ def determine_test_result(fn, opts, code, out, stderr, elapsed):
out = "\n".join(out_lines)
stats = None
if code >= 0 and opts.collect_stats:
if opts.collect_stats:
stats = {}
assert out.count("Stats:") == 1
out, stats_str = out.split("Stats:")
for l in stats_str.strip().split('\n'):
k, v = l.split(':')
stats[k.strip()] = int(v)
have_stats = (stderr.count("Stats:") == 1 and stderr.count("(End of stats)") == 1)
if code >= 0:
assert have_stats
if have_stats:
assert stderr.count("Stats:") == 1
stderr, stats_str = stderr.split("Stats:")
stats_str, stderr_tail = stats_str.split("(End of stats)\n")
stderr += stderr_tail
other_stats_str, counter_str = stats_str.split("Counters:")
for l in counter_str.strip().split('\n'):
assert l.count(':') == 1, l
k, v = l.split(':')
stats[k.strip()] = int(v)
last_stderr_line = stderr.strip().split('\n')[-1]
if EXIT_CODE_ONLY:
# fools the rest of this function into thinking the output is OK & just checking the exit code.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment