From d8b1032f3595c0d4b6943fd5353833350743a5f1 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Fri, 27 Jan 2023 18:14:45 -0500 Subject: [PATCH] Test debug --- .../DecompilerDataTypeReferenceFinder.java | 52 ++++------ .../extension/datatype/finder/DtrfDbg.java | 99 +++++++++++++++++++ .../datatype/finder/VariableAccessDR.java | 31 ++++++ 3 files changed, 148 insertions(+), 34 deletions(-) create mode 100644 Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DtrfDbg.java diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java index 628c69a74c..c3f41c811e 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerDataTypeReferenceFinder.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,17 +15,13 @@ */ package ghidra.app.extension.datatype.finder; -import java.io.ByteArrayOutputStream; -import java.io.PrintWriter; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Predicate; import org.apache.commons.collections4.IterableUtils; -import org.apache.commons.lang3.StringUtils; -import generic.io.NullPrintWriter; import ghidra.app.decompiler.*; import ghidra.app.decompiler.component.DecompilerUtils; import ghidra.app.decompiler.parallel.*; @@ -37,7 +33,8 @@ import ghidra.program.model.address.Address; import ghidra.program.model.data.BuiltInDataType; import ghidra.program.model.data.DataType; import ghidra.program.model.listing.*; -import ghidra.util.*; +import ghidra.util.Msg; +import ghidra.util.StringUtilities; import ghidra.util.datastruct.SetAccumulator; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -287,8 +284,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde private DataType dataType; private FieldMatcher fieldMatcher; - private ByteArrayOutputStream debugBytes = new ByteArrayOutputStream(); - private PrintWriter debugWriter = new PrintWriter(debugBytes); + private String dbgPrefix; DecompilerDataTypeFinder(DecompileResults results, Function function, DataType dataType, FieldMatcher fieldMatcher) { @@ -297,12 +293,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde this.dataType = dataType; this.fieldMatcher = fieldMatcher; - if (SystemUtilities.isInTestingMode()) { - debugWriter = new PrintWriter(debugBytes); - } - else { - debugWriter = new NullPrintWriter(); - } + this.dbgPrefix = "f: " + function + "\n\t"; } List findUsage() { @@ -326,21 +317,16 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde return; } - debugWriter.println("f: " + function + "\n\tchecking vars..."); + DtrfDbg.println(dbgPrefix + "checking vars..."); List variables = findVariableReferences(tokens); - debugWriter.println("f: " + function + "\n\t...done checking"); - - debugWriter.flush(); - String output = debugBytes.toString(); - if (!StringUtils.isBlank(output)) { - Msg.debug(this, "Final Debug:\n" + output); - } + DtrfDbg.println(dbgPrefix + "DONE searching decompilation\nMatching results"); variables.forEach(v -> matchUsage(v, results)); } /** Finds any search input match in the given reference */ private void matchUsage(DecompilerReference reference, List results) { + DtrfDbg.println("Checking " + reference); reference.accumulateMatches(dataType, fieldMatcher, results); } @@ -388,12 +374,12 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde VariableAccessDR access = null; for (ClangToken token : filteredTokens) { - debugWriter.println("f: " + function + "\n\tchecking token: " + token); + DtrfDbg.println(dbgPrefix + "checking token: " + token); if (token instanceof ClangTypeToken) { if (token.Parent() instanceof ClangReturnType) { - debugWriter.println("f: " + function + "\n\t\treturn type: " + line); + DtrfDbg.println(dbgPrefix + "\treturn type: " + line); results.add(new ReturnTypeDR(line, (ClangTypeToken) token)); } @@ -401,13 +387,13 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde // Note: variable refs will get their variable in an upcoming token if (isFunctionPrototype(token.Parent())) { - debugWriter.println("f: " + function + "\n\t\tparameter: " + line); + DtrfDbg.println(dbgPrefix + "\tparameter: " + line); declaration = new ParameterDR(line, (ClangTypeToken) token); } else { - debugWriter.println("f: " + function + "\n\t\tlocal var: " + line); + DtrfDbg.println(dbgPrefix + "\tlocal var: " + line); declaration = new LocalVariableDR(line, (ClangTypeToken) token); } @@ -416,7 +402,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde } else { - debugWriter.println("f: " + function + "\n\t\tadding a cast"); + DtrfDbg.println(dbgPrefix + "\tadding a cast"); // Assumption: this is a cast inside of a ClangStatement // Assumption: there can be multiple casts concatenated @@ -438,8 +424,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde // if (declaration != null) { - debugWriter.println( - "f: " + function + "\n\t\thave declaration - " + declaration); + DtrfDbg.println(dbgPrefix + "\thave declaration - " + declaration); declaration.setVariable((ClangVariableToken) token); declaration = null; @@ -447,8 +432,7 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde else { if (access == null || access.getVariable() != null) { - debugWriter.println( - "f: " + function + "\n\t\tcreating variable access: " + line); + DtrfDbg.println(dbgPrefix + "\tcreating variable access: " + line); access = new VariableAccessDR(line); results.add(access); @@ -481,8 +465,8 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde ClangFieldToken field = (ClangFieldToken) token; if (typesDoNotMatch(access, field)) { - debugWriter.println("f: " + function + - "\n\t\tcreating an anonymous variable access: " + line); + DtrfDbg.println( + dbgPrefix + "\tcreating an anonymous variable access: " + line); // this can happen when a field is used anonymously, such as directly // after a nested array index operation diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DtrfDbg.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DtrfDbg.java new file mode 100644 index 0000000000..199bb656f6 --- /dev/null +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DtrfDbg.java @@ -0,0 +1,99 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.extension.datatype.finder; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.util.*; + +import org.apache.commons.lang3.StringUtils; + +import generic.io.NullPrintWriter; +import ghidra.util.Msg; + +/** + * A package utility class to allow for tests to selectively enable debug output. This class is + * used instead of generic logging with the intent that this class will be removed when the bug(s) + * are fixed. + */ +class DtrfDbg { + + private static ByteArrayOutputStream debugBytes; + private static PrintWriter debugWriter = new NullPrintWriter(); + + private static List clientFilters = new ArrayList<>(); + + DtrfDbg() { + // static class + } + + static void enable() { + debugBytes = new ByteArrayOutputStream(); + debugWriter = new PrintWriter(debugBytes); + } + + private static void close() { + debugWriter.close(); + debugWriter = new NullPrintWriter(); + } + + static void disable(boolean write) { + + if (!write) { + close(); + return; + } + + debugWriter.flush(); + String output = debugBytes.toString(); + if (!StringUtils.isBlank(output)) { + Msg.debug(DtrfDbg.class, "\n\nFinal Debug:\n" + output); + } + + close(); + } + + /** + * Sets filters that will be checked against the {@code toString()} of each client. The + * filtering is a case-sensitive 'contains' check. + * @param filters the text + */ + static void setClientToStringFilters(String... filters) { + clientFilters.clear(); + clientFilters.addAll(Arrays.asList(filters)); + } + + static void println(String s) { + debugWriter.println(s); + } + + static void println(Object client, String s) { + if (!passesFilter(client)) { + return; + } + + debugWriter.println(s); + } + + private static boolean passesFilter(Object client) { + if (client == null || clientFilters.isEmpty()) { + return true; + } + + String asString = client.toString(); + return clientFilters.stream().anyMatch(s -> asString.contains(s)); + } +} diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java index 995aad974e..c7b7981f9f 100644 --- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java +++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/VariableAccessDR.java @@ -106,22 +106,28 @@ public class VariableAccessDR extends DecompilerReference { private DecompilerVariable getMatch(DataType dt, FieldMatcher fieldMatcher, DecompilerVariable var, DecompilerVariable potentialField) { + String indent = "\t\t"; + // Note: for now, I ignore the precedence of casting; if any cast type is a match, then // signal hooray boolean searchForField = !fieldMatcher.isIgnored(); DecompilerVariable fieldVar = searchForField ? potentialField : null; DecompilerVariable match = getMatchingVarialbe(dt, var, fieldVar); if (match == null) { + DtrfDbg.println(this, indent + "NO MATCHING VARIABLE"); return null; // wrong type, nothing to do } // Matches on the type, does the field match? if (fieldMatcher.isIgnored()) { + DtrfDbg.println(this, indent + "field macher is ignored; returning match"); return match; // no field to match } if (potentialField == null) { + DtrfDbg.println(this, indent + "No potential field to match; name / offset match?"); + // check for the case where we have not been passed a 'potential field', but the given // 'var' is itself may be the field we seek, such as in an if statement like this: // if (color == RED) @@ -129,34 +135,52 @@ public class VariableAccessDR extends DecompilerReference { String name = var.getName(); int offset = var.getOffset(); if (fieldMatcher.matches(name, offset)) { + DtrfDbg.println(this, indent + "\tfield matcher matched on variable: " + var); return var; } + DtrfDbg.println(this, indent + "\tNO FIELD MATCHER MATCH"); return null; // we seek a field, but there is none } + DtrfDbg.println(this, indent + "Checking 'potential field' match..."); + String name = potentialField.getName(); int offset = potentialField.getOffset(); if (fieldMatcher.matches(name, offset)) { + DtrfDbg.println(this, indent + "\tMATCHED"); return match; } + DtrfDbg.println(this, indent + "\tNO MATCH"); return null; } private DecompilerVariable getMatchingVarialbe(DataType dt, DecompilerVariable var, DecompilerVariable potentialField) { + String indent = "\t\t\t"; + + DtrfDbg.println(this, indent + "Checking for matching variable; any casts?"); List castVariables = var.getCasts(); for (DecompilerVariable cast : castVariables) { if (matchesType(cast, dt)) { + DtrfDbg.println(this, indent + "MATCHED cast: " + cast); return cast; } } + String dtString = dt == null ? "null" : dt.toString(); + DtrfDbg.println(this, + indent + "No matched casts; checking type against var:\n" + + StringUtilities.indentLines("type: " + dtString, indent + "\t") + "\n" + + StringUtilities.indentLines("var: " + var.toString(), indent + "\t")); if (matchesType(var, dt)) { + DtrfDbg.println(this, indent + "MATCHED type: "); return var; } + DtrfDbg.println(this, indent + "Type did not match; checking High Variable: "); + // // Unusual Code Alert! // It is a bit odd to check the field when you are looking for the type that contains the @@ -170,10 +194,12 @@ public class VariableAccessDR extends DecompilerReference { HighVariable highVariable = var.variable.getHighVariable(); if (highVariable instanceof HighGlobal) { if (matchesParentType(potentialField, dt)) { + DtrfDbg.println(this, indent + "MATCHED on parent type: " + dt); return potentialField; } } + DtrfDbg.println(this, indent + "NOT MATCHED"); return null; } @@ -188,7 +214,11 @@ public class VariableAccessDR extends DecompilerReference { } private boolean matchesType(DecompilerVariable var, DataType dt) { + + String indent = "\t\t\t\t"; + if (var == null) { + DtrfDbg.println(this, indent + "Types Match? no variable to check"); return false; } @@ -196,6 +226,7 @@ public class VariableAccessDR extends DecompilerReference { if (varType == null) { // it seems odd to me that there is no type, but I have seen this in the case // statement of a switch + DtrfDbg.println(this, indent + "ypes Match? no variable TYPE to check"); return false; } boolean matches = isEqual(varType, dt);