* Keep more statistics about stack space usage.
* Reduce stack space usage.
This commit is contained in:
		
							parent
							
								
									b7b3dd55f9
								
							
						
					
					
						commit
						f3dc7ab877
					
				
					 2 changed files with 78 additions and 43 deletions
				
			
		|  | @ -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++; | ||||||
|  | @ -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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue