aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2010-04-09 12:00:49 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2010-04-09 12:00:49 +0000
commitf3dc7ab877c736ec1500f6b503ab71a62ce90305 (patch)
tree5807ecef874e9392d1e9d1a6e2602d03240c594e /src
parentb7b3dd55f9e30e2eccb54aca22830852c5c2b514 (diff)
* Keep more statistics about stack space usage.
* Reduce stack space usage.
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/eval.cc105
-rw-r--r--src/libexpr/eval.hh16
2 files changed, 78 insertions, 43 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 2d22a4fdc..56a73b494 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -93,7 +93,8 @@ string showType(Value & v)
EvalState::EvalState() : baseEnv(allocEnv())
{
- nrValues = nrEnvs = nrEvaluated = 0;
+ nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0;
+ deepestStack = (char *) -1;
initNixExprHelpers();
@@ -103,6 +104,12 @@ EvalState::EvalState() : baseEnv(allocEnv())
}
+EvalState::~EvalState()
+{
+ assert(recursionDepth == 0);
+}
+
+
void EvalState::addConstant(const string & name, Value & v)
{
baseEnv.bindings[toATerm(name)] = v;
@@ -141,6 +148,11 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
throw EvalError(format(s) % s2);
}
+LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2, const string & s3))
+{
+ throw EvalError(format(s) % s2 % s3);
+}
+
LocalNoInlineNoReturn(void throwTypeError(const char * s))
{
throw TypeError(s);
@@ -151,6 +163,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
throw TypeError(format(s) % s2);
}
+LocalNoInlineNoReturn(void throwAssertionError(const char * s, const string & s2))
+{
+ throw TypeError(format(s) % s2);
+}
+
LocalNoInline(void addErrorPrefix(Error & e, const char * s))
{
e.addPrefix(s);
@@ -234,7 +251,7 @@ static Value * lookupVar(Env * env, Sym name)
}
#endif
- throw Error(format("undefined variable `%1%'") % aterm2String(name));
+ throwEvalError("undefined variable `%1%'", aterm2String(name));
}
@@ -295,21 +312,35 @@ void EvalState::evalFile(const Path & path, Value & v)
try {
eval(e, v);
} catch (Error & e) {
- e.addPrefix(format("while evaluating the file `%1%':\n")
- % path);
+ addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
throw;
}
}
-static char * deepestStack = (char *) -1; /* for measuring stack usage */
+struct RecursionCounter
+{
+ EvalState & state;
+ RecursionCounter(EvalState & state) : state(state)
+ {
+ state.recursionDepth++;
+ if (state.recursionDepth > state.maxRecursionDepth)
+ state.maxRecursionDepth = state.recursionDepth;
+ }
+ ~RecursionCounter()
+ {
+ state.recursionDepth--;
+ }
+};
void EvalState::eval(Env & env, Expr e, Value & v)
{
/* When changing this function, make sure that you don't cause a
(large) increase in stack consumption! */
-
+
+ /* !!! Disable this eventually. */
+ RecursionCounter r(*this);
char x;
if (&x < deepestStack) deepestStack = &x;
@@ -482,8 +513,7 @@ void EvalState::eval(Env & env, Expr e, Value & v)
}
if (isPath && !context.empty())
- throw EvalError(format("a string that refers to a store path cannot be appended to a path, in `%1%'")
- % s.str());
+ throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
if (isPath)
mkPath(v, s.str().c_str());
@@ -498,7 +528,7 @@ void EvalState::eval(Env & env, Expr e, Value & v)
/* Assertions. */
else if (matchAssert(e, e1, e2, pos)) {
if (!evalBool(env, e1))
- throw AssertionError(format("assertion failed at %1%") % showPos(pos));
+ throwAssertionError("assertion failed at %1%", showPos(pos));
eval(env, e2, v);
}
@@ -538,7 +568,7 @@ void EvalState::eval(Env & env, Expr e, Value & v)
mkBool(v, vAttrs.attrs->find(name) != vAttrs.attrs->end());
}
- else throw Error("unsupported term");
+ else abort();
}
@@ -615,8 +645,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
if (j == arg.attrs->end()) {
if (!matchDefaultValue(def2, def)) def = 0;
- if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
- % aterm2String(name));
+ if (def == 0) throwTypeError("the argument named `%1%' required by the function is missing",
+ aterm2String(name));
mkThunk(v, env2, def);
} else {
attrsUsed++;
@@ -629,7 +659,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
TODO: show the names of the expected/unexpected
arguments. */
if (ellipsis == eFalse && attrsUsed != arg.attrs->size())
- throw TypeError("function called with unexpected argument");
+ throwTypeError("function called with unexpected argument");
}
else abort();
@@ -661,8 +691,7 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res
if (j != args.end())
(*actualArgs.attrs)[name] = j->second;
else if (!matchDefaultValue(def2, def))
- throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1% ')")
- % aterm2String(name));
+ throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", aterm2String(name));
}
callFunction(fun, actualArgs, res);
@@ -680,7 +709,7 @@ bool EvalState::evalBool(Env & env, Expr e)
Value v;
eval(env, e, v);
if (v.type != tBool)
- throw TypeError(format("value is %1% while a Boolean was expected") % showType(v));
+ throwTypeError("value is %1% while a Boolean was expected", showType(v));
return v.boolean;
}
@@ -704,7 +733,7 @@ void EvalState::forceValue(Value & v)
else if (v.type == tApp)
callFunction(*v.app.left, *v.app.right, v);
else if (v.type == tBlackhole)
- throw EvalError("infinite recursion encountered");
+ throwEvalError("infinite recursion encountered");
}
@@ -728,7 +757,7 @@ int EvalState::forceInt(Value & v)
{
forceValue(v);
if (v.type != tInt)
- throw TypeError(format("value is %1% while an integer was expected") % showType(v));
+ throwTypeError("value is %1% while an integer was expected", showType(v));
return v.integer;
}
@@ -737,7 +766,7 @@ bool EvalState::forceBool(Value & v)
{
forceValue(v);
if (v.type != tBool)
- throw TypeError(format("value is %1% while a Boolean was expected") % showType(v));
+ throwTypeError("value is %1% while a Boolean was expected", showType(v));
return v.boolean;
}
@@ -746,7 +775,7 @@ void EvalState::forceAttrs(Value & v)
{
forceValue(v);
if (v.type != tAttrs)
- throw TypeError(format("value is %1% while an attribute set was expected") % showType(v));
+ throwTypeError("value is %1% while an attribute set was expected", showType(v));
}
@@ -754,7 +783,7 @@ void EvalState::forceList(Value & v)
{
forceValue(v);
if (v.type != tList)
- throw TypeError(format("value is %1% while a list was expected") % showType(v));
+ throwTypeError("value is %1% while a list was expected", showType(v));
}
@@ -762,7 +791,7 @@ void EvalState::forceFunction(Value & v)
{
forceValue(v);
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp)
- throw TypeError(format("value is %1% while a function was expected") % showType(v));
+ throwTypeError("value is %1% while a function was expected", showType(v));
}
@@ -770,7 +799,7 @@ string EvalState::forceString(Value & v)
{
forceValue(v);
if (v.type != tString)
- throw TypeError(format("value is %1% while a string was expected") % showType(v));
+ throwTypeError("value is %1% while a string was expected", showType(v));
return string(v.string.s);
}
@@ -778,10 +807,9 @@ string EvalState::forceString(Value & v)
string EvalState::forceString(Value & v, PathSet & context)
{
string s = forceString(v);
- if (v.string.context) {
+ if (v.string.context)
for (const char * * p = v.string.context; *p; ++p)
context.insert(*p);
- }
return s;
}
@@ -790,8 +818,8 @@ string EvalState::forceStringNoCtx(Value & v)
{
string s = forceString(v);
if (v.string.context)
- throw EvalError(format("the string `%1%' is not allowed to refer to a store path (such as `%2%')")
- % v.string.s % v.string.context[0]);
+ throwEvalError("the string `%1%' is not allowed to refer to a store path (such as `%2%')",
+ v.string.s, v.string.context[0]);
return s;
}
@@ -824,8 +852,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
if (!copyToStore) return path;
if (nix::isDerivation(path))
- throw EvalError(format("file names are not allowed to end in `%1%'")
- % drvExtension);
+ throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
Path dstPath;
if (srcToStore[path] != "")
@@ -881,7 +908,7 @@ Path EvalState::coerceToPath(Value & v, PathSet & context)
{
string path = coerceToString(v, context, false, false);
if (path == "" || path[0] != '/')
- throw EvalError(format("string `%1%' doesn't represent an absolute path") % path);
+ throwEvalError("string `%1%' doesn't represent an absolute path", path);
return path;
}
@@ -932,7 +959,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
return false;
default:
- throw Error(format("cannot compare %1% with %2%") % showType(v1) % showType(v2));
+ throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2));
}
}
@@ -941,14 +968,14 @@ void EvalState::printStats()
{
char x;
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
- printMsg(showStats ? lvlInfo : lvlDebug,
- format("evaluated %1% expressions, used %2% bytes of stack space, allocated %3% values, allocated %4% environments")
- % nrEvaluated
- % (&x - deepestStack)
- % nrValues
- % nrEnvs);
- if (showStats)
- printATermMapStats();
+ Verbosity v = showStats ? lvlInfo : lvlDebug;
+ printMsg(v, "evaluation statistics:");
+ printMsg(v, format(" expressions evaluated: %1%") % nrEvaluated);
+ printMsg(v, format(" stack space used: %1% bytes") % (&x - deepestStack));
+ printMsg(v, format(" max eval() nesting depth: %1%") % maxRecursionDepth);
+ printMsg(v, format(" stack space per eval() level: %1% bytes") % ((&x - deepestStack) / (float) maxRecursionDepth));
+ printMsg(v, format(" values allocated: %1%") % nrValues);
+ printMsg(v, format(" environments allocated: %1%") % nrEnvs);
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 0ea474447..fb4dd802e 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -144,10 +144,6 @@ public:
private:
SrcToStore srcToStore;
- unsigned long nrValues;
- unsigned long nrEnvs;
- unsigned long nrEvaluated;
-
bool allowUnsafeEquality;
ATermMap parseTrees;
@@ -155,6 +151,7 @@ private:
public:
EvalState();
+ ~EvalState();
/* Evaluate an expression read from the given file to normal
form. */
@@ -242,6 +239,17 @@ public:
/* Print statistics. */
void printStats();
+
+private:
+
+ unsigned long nrValues;
+ unsigned long nrEnvs;
+ unsigned long nrEvaluated;
+ unsigned int recursionDepth;
+ unsigned int maxRecursionDepth;
+ char * deepestStack; /* for measuring stack usage */
+
+ friend class RecursionCounter;
};