forked from lix-project/lix
* Keep more statistics about stack space usage.
* Reduce stack space usage.
This commit is contained in:
parent
b7b3dd55f9
commit
f3dc7ab877
|
@ -93,7 +93,8 @@ string showType(Value & v)
|
||||||
|
|
||||||
EvalState::EvalState() : baseEnv(allocEnv())
|
EvalState::EvalState() : baseEnv(allocEnv())
|
||||||
{
|
{
|
||||||
nrValues = nrEnvs = nrEvaluated = 0;
|
nrValues = nrEnvs = nrEvaluated = recursionDepth = maxRecursionDepth = 0;
|
||||||
|
deepestStack = (char *) -1;
|
||||||
|
|
||||||
initNixExprHelpers();
|
initNixExprHelpers();
|
||||||
|
|
||||||
|
@ -103,6 +104,12 @@ EvalState::EvalState() : baseEnv(allocEnv())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
EvalState::~EvalState()
|
||||||
|
{
|
||||||
|
assert(recursionDepth == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::addConstant(const string & name, Value & v)
|
void EvalState::addConstant(const string & name, Value & v)
|
||||||
{
|
{
|
||||||
baseEnv.bindings[toATerm(name)] = v;
|
baseEnv.bindings[toATerm(name)] = v;
|
||||||
|
@ -141,6 +148,11 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const string & s2))
|
||||||
throw EvalError(format(s) % 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))
|
LocalNoInlineNoReturn(void throwTypeError(const char * s))
|
||||||
{
|
{
|
||||||
throw TypeError(s);
|
throw TypeError(s);
|
||||||
|
@ -151,6 +163,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
|
||||||
throw TypeError(format(s) % 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))
|
LocalNoInline(void addErrorPrefix(Error & e, const char * s))
|
||||||
{
|
{
|
||||||
e.addPrefix(s);
|
e.addPrefix(s);
|
||||||
|
@ -234,7 +251,7 @@ static Value * lookupVar(Env * env, Sym name)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
throw Error(format("undefined variable `%1%'") % aterm2String(name));
|
throwEvalError("undefined variable `%1%'", aterm2String(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -295,14 +312,26 @@ void EvalState::evalFile(const Path & path, Value & v)
|
||||||
try {
|
try {
|
||||||
eval(e, v);
|
eval(e, v);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addPrefix(format("while evaluating the file `%1%':\n")
|
addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
|
||||||
% path);
|
|
||||||
throw;
|
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)
|
void EvalState::eval(Env & env, Expr e, Value & v)
|
||||||
|
@ -310,6 +339,8 @@ void EvalState::eval(Env & env, Expr e, Value & v)
|
||||||
/* When changing this function, make sure that you don't cause a
|
/* When changing this function, make sure that you don't cause a
|
||||||
(large) increase in stack consumption! */
|
(large) increase in stack consumption! */
|
||||||
|
|
||||||
|
/* !!! Disable this eventually. */
|
||||||
|
RecursionCounter r(*this);
|
||||||
char x;
|
char x;
|
||||||
if (&x < deepestStack) deepestStack = &x;
|
if (&x < deepestStack) deepestStack = &x;
|
||||||
|
|
||||||
|
@ -482,8 +513,7 @@ void EvalState::eval(Env & env, Expr e, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPath && !context.empty())
|
if (isPath && !context.empty())
|
||||||
throw EvalError(format("a string that refers to a store path cannot be appended to a path, in `%1%'")
|
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
|
||||||
% s.str());
|
|
||||||
|
|
||||||
if (isPath)
|
if (isPath)
|
||||||
mkPath(v, s.str().c_str());
|
mkPath(v, s.str().c_str());
|
||||||
|
@ -498,7 +528,7 @@ void EvalState::eval(Env & env, Expr e, Value & v)
|
||||||
/* Assertions. */
|
/* Assertions. */
|
||||||
else if (matchAssert(e, e1, e2, pos)) {
|
else if (matchAssert(e, e1, e2, pos)) {
|
||||||
if (!evalBool(env, e1))
|
if (!evalBool(env, e1))
|
||||||
throw AssertionError(format("assertion failed at %1%") % showPos(pos));
|
throwAssertionError("assertion failed at %1%", showPos(pos));
|
||||||
eval(env, e2, v);
|
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());
|
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 (j == arg.attrs->end()) {
|
||||||
if (!matchDefaultValue(def2, def)) def = 0;
|
if (!matchDefaultValue(def2, def)) def = 0;
|
||||||
if (def == 0) throw TypeError(format("the argument named `%1%' required by the function is missing")
|
if (def == 0) throwTypeError("the argument named `%1%' required by the function is missing",
|
||||||
% aterm2String(name));
|
aterm2String(name));
|
||||||
mkThunk(v, env2, def);
|
mkThunk(v, env2, def);
|
||||||
} else {
|
} else {
|
||||||
attrsUsed++;
|
attrsUsed++;
|
||||||
|
@ -629,7 +659,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||||
TODO: show the names of the expected/unexpected
|
TODO: show the names of the expected/unexpected
|
||||||
arguments. */
|
arguments. */
|
||||||
if (ellipsis == eFalse && attrsUsed != arg.attrs->size())
|
if (ellipsis == eFalse && attrsUsed != arg.attrs->size())
|
||||||
throw TypeError("function called with unexpected argument");
|
throwTypeError("function called with unexpected argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
else abort();
|
else abort();
|
||||||
|
@ -661,8 +691,7 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res
|
||||||
if (j != args.end())
|
if (j != args.end())
|
||||||
(*actualArgs.attrs)[name] = j->second;
|
(*actualArgs.attrs)[name] = j->second;
|
||||||
else if (!matchDefaultValue(def2, def))
|
else if (!matchDefaultValue(def2, def))
|
||||||
throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1% ')")
|
throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", aterm2String(name));
|
||||||
% aterm2String(name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callFunction(fun, actualArgs, res);
|
callFunction(fun, actualArgs, res);
|
||||||
|
@ -680,7 +709,7 @@ bool EvalState::evalBool(Env & env, Expr e)
|
||||||
Value v;
|
Value v;
|
||||||
eval(env, e, v);
|
eval(env, e, v);
|
||||||
if (v.type != tBool)
|
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;
|
return v.boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -704,7 +733,7 @@ void EvalState::forceValue(Value & v)
|
||||||
else if (v.type == tApp)
|
else if (v.type == tApp)
|
||||||
callFunction(*v.app.left, *v.app.right, v);
|
callFunction(*v.app.left, *v.app.right, v);
|
||||||
else if (v.type == tBlackhole)
|
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);
|
forceValue(v);
|
||||||
if (v.type != tInt)
|
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;
|
return v.integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -737,7 +766,7 @@ bool EvalState::forceBool(Value & v)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v);
|
||||||
if (v.type != tBool)
|
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;
|
return v.boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,7 +775,7 @@ void EvalState::forceAttrs(Value & v)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v);
|
||||||
if (v.type != tAttrs)
|
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);
|
forceValue(v);
|
||||||
if (v.type != tList)
|
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);
|
forceValue(v);
|
||||||
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp)
|
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);
|
forceValue(v);
|
||||||
if (v.type != tString)
|
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);
|
return string(v.string.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -778,10 +807,9 @@ string EvalState::forceString(Value & v)
|
||||||
string EvalState::forceString(Value & v, PathSet & context)
|
string EvalState::forceString(Value & v, PathSet & context)
|
||||||
{
|
{
|
||||||
string s = forceString(v);
|
string s = forceString(v);
|
||||||
if (v.string.context) {
|
if (v.string.context)
|
||||||
for (const char * * p = v.string.context; *p; ++p)
|
for (const char * * p = v.string.context; *p; ++p)
|
||||||
context.insert(*p);
|
context.insert(*p);
|
||||||
}
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,8 +818,8 @@ string EvalState::forceStringNoCtx(Value & v)
|
||||||
{
|
{
|
||||||
string s = forceString(v);
|
string s = forceString(v);
|
||||||
if (v.string.context)
|
if (v.string.context)
|
||||||
throw EvalError(format("the string `%1%' is not allowed to refer to a store path (such as `%2%')")
|
throwEvalError("the string `%1%' is not allowed to refer to a store path (such as `%2%')",
|
||||||
% v.string.s % v.string.context[0]);
|
v.string.s, v.string.context[0]);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -824,8 +852,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
||||||
if (!copyToStore) return path;
|
if (!copyToStore) return path;
|
||||||
|
|
||||||
if (nix::isDerivation(path))
|
if (nix::isDerivation(path))
|
||||||
throw EvalError(format("file names are not allowed to end in `%1%'")
|
throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
|
||||||
% drvExtension);
|
|
||||||
|
|
||||||
Path dstPath;
|
Path dstPath;
|
||||||
if (srcToStore[path] != "")
|
if (srcToStore[path] != "")
|
||||||
|
@ -881,7 +908,7 @@ Path EvalState::coerceToPath(Value & v, PathSet & context)
|
||||||
{
|
{
|
||||||
string path = coerceToString(v, context, false, false);
|
string path = coerceToString(v, context, false, false);
|
||||||
if (path == "" || path[0] != '/')
|
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;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -932,7 +959,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
default:
|
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;
|
char x;
|
||||||
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
|
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
|
||||||
printMsg(showStats ? lvlInfo : lvlDebug,
|
Verbosity v = showStats ? lvlInfo : lvlDebug;
|
||||||
format("evaluated %1% expressions, used %2% bytes of stack space, allocated %3% values, allocated %4% environments")
|
printMsg(v, "evaluation statistics:");
|
||||||
% nrEvaluated
|
printMsg(v, format(" expressions evaluated: %1%") % nrEvaluated);
|
||||||
% (&x - deepestStack)
|
printMsg(v, format(" stack space used: %1% bytes") % (&x - deepestStack));
|
||||||
% nrValues
|
printMsg(v, format(" max eval() nesting depth: %1%") % maxRecursionDepth);
|
||||||
% nrEnvs);
|
printMsg(v, format(" stack space per eval() level: %1% bytes") % ((&x - deepestStack) / (float) maxRecursionDepth));
|
||||||
if (showStats)
|
printMsg(v, format(" values allocated: %1%") % nrValues);
|
||||||
printATermMapStats();
|
printMsg(v, format(" environments allocated: %1%") % nrEnvs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -144,10 +144,6 @@ public:
|
||||||
private:
|
private:
|
||||||
SrcToStore srcToStore;
|
SrcToStore srcToStore;
|
||||||
|
|
||||||
unsigned long nrValues;
|
|
||||||
unsigned long nrEnvs;
|
|
||||||
unsigned long nrEvaluated;
|
|
||||||
|
|
||||||
bool allowUnsafeEquality;
|
bool allowUnsafeEquality;
|
||||||
|
|
||||||
ATermMap parseTrees;
|
ATermMap parseTrees;
|
||||||
|
@ -155,6 +151,7 @@ private:
|
||||||
public:
|
public:
|
||||||
|
|
||||||
EvalState();
|
EvalState();
|
||||||
|
~EvalState();
|
||||||
|
|
||||||
/* Evaluate an expression read from the given file to normal
|
/* Evaluate an expression read from the given file to normal
|
||||||
form. */
|
form. */
|
||||||
|
@ -242,6 +239,17 @@ public:
|
||||||
|
|
||||||
/* Print statistics. */
|
/* Print statistics. */
|
||||||
void printStats();
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue