Test debug

This commit is contained in:
dragonmacher
2023-01-27 18:14:45 -05:00
parent c78a9c35e1
commit d8b1032f35
3 changed files with 148 additions and 34 deletions

View File

@@ -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<DataTypeReference> findUsage() {
@@ -326,21 +317,16 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
return;
}
debugWriter.println("f: " + function + "\n\tchecking vars...");
DtrfDbg.println(dbgPrefix + "checking vars...");
List<DecompilerReference> 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<DataTypeReference> 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

View File

@@ -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<String> 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));
}
}

View File

@@ -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<DecompilerVariable> 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);