Add config.trace=substitutions mode

Dumps a bunch of verbose output about how substitutions
are being resolved.
This commit is contained in:
Havoc Pennington 2014-07-04 14:34:02 -04:00
parent 99afd721a5
commit 91497e4a1e
8 changed files with 132 additions and 4 deletions

View File

@ -171,11 +171,23 @@ final class ConfigConcatenation extends AbstractConfigValue implements Unmergeab
@Override
AbstractConfigValue resolveSubstitutions(ResolveContext context) throws NotPossibleToResolve {
if (ConfigImpl.traceSubstitutionsEnabled()) {
int indent = context.depth() + 2;
ConfigImpl.trace(indent - 1, "concatenation has " + pieces.size() + " pieces:");
int count = 0;
for (AbstractConfigValue v : pieces) {
ConfigImpl.trace(indent, count + ": " + v);
count += 1;
}
}
List<AbstractConfigValue> resolved = new ArrayList<AbstractConfigValue>(pieces.size());
for (AbstractConfigValue p : pieces) {
// to concat into a string we have to do a full resolve,
// so unrestrict the context
AbstractConfigValue r = context.unrestricted().resolve(p);
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), "resolved concat piece to " + r);
if (r == null) {
// it was optional... omit
} else {

View File

@ -62,6 +62,16 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
// static method also used by ConfigDelayedMergeObject
static AbstractConfigValue resolveSubstitutions(ReplaceableMergeStack replaceable,
List<AbstractConfigValue> stack, ResolveContext context) throws NotPossibleToResolve {
if (ConfigImpl.traceSubstitutionsEnabled()) {
int indent = context.depth() + 2;
ConfigImpl.trace(indent - 1, "delayed merge stack has " + stack.size() + " items:");
int count = 0;
for (AbstractConfigValue v : stack) {
ConfigImpl.trace(indent, count + ": " + v);
count += 1;
}
}
// to resolve substitutions, we need to recursively resolve
// the stack of stuff to merge, and merge the stack so
// we won't be a delayed merge anymore. If restrictToChildOrNull
@ -86,6 +96,10 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
// ConfigDelayedMerge with a value that is only
// the remainder of the stack below this one.
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth() + 1, "because item " + count
+ " in this stack is unresolved, resolving it can only look at remaining "
+ (stack.size() - count - 1) + " items");
context.source().replace((AbstractConfigValue) replaceable,
replaceable.makeReplacer(count + 1));
replaced = true;
@ -93,6 +107,9 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
AbstractConfigValue resolved;
try {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth() + 1, "resolving item " + count + " in merge stack of "
+ stack.size());
resolved = context.resolve(v);
} finally {
if (replaced)
@ -100,14 +117,20 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
}
if (resolved != null) {
if (merged == null)
if (merged == null) {
merged = resolved;
else
} else {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth() + 1, "merging " + merged + " with fallback " + resolved);
merged = merged.withFallback(resolved);
}
}
count += 1;
}
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth() + 1, "stack was merged to: " + merged);
return merged;
}
@ -129,6 +152,8 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
List<AbstractConfigValue> subStack = stack.subList(skipping, stack.size());
if (subStack.isEmpty()) {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), "Nothing else in the merge stack, can't resolve");
throw new NotPossibleToResolve(context);
} else {
// generate a new merge stack from only the remaining items

View File

@ -377,10 +377,12 @@ public class ConfigImpl {
private static class DebugHolder {
private static String LOADS = "loads";
private static String SUBSTITUTIONS = "substitutions";
private static Map<String, Boolean> loadDiagnostics() {
Map<String, Boolean> result = new HashMap<String, Boolean>();
result.put(LOADS, false);
result.put(SUBSTITUTIONS, false);
// People do -Dconfig.trace=foo,bar to enable tracing of different things
String s = System.getProperty("config.trace");
@ -391,6 +393,8 @@ public class ConfigImpl {
for (String k : keys) {
if (k.equals(LOADS)) {
result.put(LOADS, true);
} else if (k.equals(SUBSTITUTIONS)) {
result.put(SUBSTITUTIONS, true);
} else {
System.err.println("config.trace property contains unknown trace topic '"
+ k + "'");
@ -403,10 +407,15 @@ public class ConfigImpl {
private static final Map<String, Boolean> diagnostics = loadDiagnostics();
private static final boolean traceLoadsEnabled = diagnostics.get(LOADS);
private static final boolean traceSubstitutionsEnabled = diagnostics.get(SUBSTITUTIONS);
static boolean traceLoadsEnabled() {
return traceLoadsEnabled;
}
static boolean traceSubstitutionsEnabled() {
return traceSubstitutionsEnabled;
}
}
/** For use ONLY by library internals, DO NOT TOUCH not guaranteed ABI */
@ -418,10 +427,26 @@ public class ConfigImpl {
}
}
public static boolean traceSubstitutionsEnabled() {
try {
return DebugHolder.traceSubstitutionsEnabled();
} catch (ExceptionInInitializerError e) {
throw ConfigImplUtil.extractInitializerError(e);
}
}
public static void trace(String message) {
System.err.println(message);
}
public static void trace(int indentLevel, String message) {
while (indentLevel > 0) {
System.err.print(" ");
indentLevel -= 1;
}
System.err.println(message);
}
// the basic idea here is to add the "what" and have a canonical
// toplevel error message. the "original" exception may however have extra
// detail about what happened. call this if you have a better "what" than

View File

@ -72,6 +72,9 @@ final class ConfigReference extends AbstractConfigValue implements Unmergeable {
try {
v = context.source().lookupSubst(context, expr, prefixLength);
} catch (NotPossibleToResolve e) {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(),
"not possible to resolve " + expr + ", cycle involved: " + e.traceString());
if (expr.optional())
v = null;
else

View File

@ -36,4 +36,9 @@ final class MemoKey {
return false;
}
}
@Override
public final String toString() {
return "MemoKey(" + value + "," + restrictToChildOrNull + ")";
}
}

View File

@ -43,6 +43,8 @@ final class ResolveContext {
// in error messages if nothing else
this(new ResolveSource(root), new ResolveMemos(), options, restrictToChild,
new ArrayList<SubstitutionExpression>());
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace("ResolveContext at root " + root + " restrict to child " + restrictToChild);
}
ResolveSource source() {
@ -73,11 +75,15 @@ final class ResolveContext {
}
void trace(SubstitutionExpression expr) {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(depth(), "pushing expression " + expr);
expressionTrace.add(expr);
}
void untrace() {
expressionTrace.remove(expressionTrace.size() - 1);
SubstitutionExpression expr = expressionTrace.remove(expressionTrace.size() - 1);
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(depth(), "popped expression " + expr);
}
String traceString() {
@ -92,7 +98,14 @@ final class ResolveContext {
return sb.toString();
}
int depth() {
return expressionTrace.size();
}
AbstractConfigValue resolve(AbstractConfigValue original) throws NotPossibleToResolve {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(depth(), "resolving " + original);
// a fully-resolved (no restrictToChild) object can satisfy a
// request for a restricted object, so always check that first.
final MemoKey fullKey = new MemoKey(original, null);
@ -109,15 +122,26 @@ final class ResolveContext {
}
if (cached != null) {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(depth(), "using cached resolution " + cached + " for " + original);
return cached;
} else {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(depth(), "not found in cache, resolving " + original);
AbstractConfigValue resolved = source.resolveCheckingReplacement(this, original);
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(depth(), "resolved to " + resolved + " from " + original);
if (resolved == null || resolved.resolveStatus() == ResolveStatus.RESOLVED) {
// if the resolved object is fully resolved by resolving
// only the restrictToChildOrNull, then it can be cached
// under fullKey since the child we were restricted to
// turned out to be the only unresolved thing.
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(depth(), "caching " + fullKey + " result " + resolved);
memos.put(fullKey, resolved);
} else {
// if we have an unresolved object then either we did a
@ -128,8 +152,14 @@ final class ResolveContext {
throw new ConfigException.BugOrBroken(
"restrictedKey should not be null here");
}
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(depth(), "caching " + restrictedKey + " result " + resolved);
memos.put(restrictedKey, resolved);
} else if (options().getAllowUnresolved()) {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(depth(), "caching " + fullKey + " result " + resolved);
memos.put(fullKey, resolved);
} else {
throw new ConfigException.BugOrBroken(

View File

@ -24,6 +24,8 @@ abstract class ResolveReplacer {
@Override
protected AbstractConfigValue makeReplacement(ResolveContext context)
throws NotPossibleToResolve {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), "Cycle detected, can't resolve");
throw new NotPossibleToResolve(context);
}
};

View File

@ -31,8 +31,13 @@ final class ResolveSource {
AbstractConfigValue lookupSubst(ResolveContext context, SubstitutionExpression subst,
int prefixLength) throws NotPossibleToResolve {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), "searching for " + subst);
context.trace(subst);
try {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), subst + " - looking up relative to file it occurred in");
// First we look up the full path, which means relative to the
// included file if we were not a root file
AbstractConfigValue result = findInObject(root, context, subst);
@ -49,19 +54,30 @@ final class ResolveSource {
context.trace(unprefixed);
if (prefixLength > 0) {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), unprefixed + " - looking up relative to parent file");
result = findInObject(root, context, unprefixed);
}
if (result == null && context.options().getUseSystemEnvironment()) {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), unprefixed + " - looking up in system environment");
result = findInObject(ConfigImpl.envVariablesAsConfigObject(), context,
unprefixed);
}
}
if (result != null) {
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), "recursively resolving " + result
+ " which was the resolution of " + subst);
result = context.resolve(result);
}
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), "resolved to " + result);
return result;
} finally {
context.untrace();
@ -87,7 +103,12 @@ final class ResolveSource {
if (replacer == null) {
return value;
} else {
return replacer.replace(context);
AbstractConfigValue replacement = replacer.replace(context);
if (ConfigImpl.traceSubstitutionsEnabled() && value != replacement) {
ConfigImpl.trace(" when looking up substitutions " + context.traceString() + " replaced " + value
+ " with " + replacement);
}
return replacement;
}
}
@ -103,10 +124,15 @@ final class ResolveSource {
if (replacement != original) {
// start over, checking if replacement was memoized
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), "for resolution, replaced " + original + " with " + replacement);
return context.resolve(replacement);
} else {
AbstractConfigValue resolved;
if (ConfigImpl.traceSubstitutionsEnabled())
ConfigImpl.trace(context.depth(), "resolving " + original + " with trace '" + context.traceString()
+ "'");
resolved = original.resolveSubstitutions(context);
return resolved;