Merge branch 'GP-213-dragonmacher-call-tree-duplicates' into Ghidra_9.2

This commit is contained in:
dragonmacher
2020-09-29 14:47:33 -04:00
2 changed files with 106 additions and 18 deletions

View File

@@ -141,4 +141,28 @@ public abstract class CallNode extends GTreeSlowLoadingNode {
} }
return false; return false;
} }
// overridden since we may have multiple children with the same function name, but in
// different namespaces
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
CallNode other = (CallNode) obj;
return getSourceAddress().equals(other.getSourceAddress());
}
@Override
public int hashCode() {
return getSourceAddress().hashCode();
}
} }

View File

@@ -42,6 +42,7 @@ import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin; import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.app.services.ProgramManager; import ghidra.app.services.ProgramManager;
import ghidra.app.util.NamespaceUtils;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder; import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB; import ghidra.program.database.ProgramDB;
@@ -158,6 +159,10 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
} }
} }
private Function function(int addr) throws Exception {
return ensureFunction(addr);
}
private void function(int from, int to) throws Exception { private void function(int from, int to) throws Exception {
ensureFunction(from); ensureFunction(from);
ensureFunction(to); ensureFunction(to);
@@ -168,16 +173,39 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
} }
} }
private void ensureFunction(long from) throws Exception { private Function function(String namespace, String name, int addr) throws Exception {
Function f = ensureFunction(addr);
tx(program, () -> {
f.setName(name, SourceType.ANALYSIS);
Namespace ns = NamespaceUtils.createNamespaceHierarchy(namespace, null, program,
SourceType.ANALYSIS);
f.setParentNamespace(ns);
});
return f;
}
private void function(Function from, Function to) {
// a bit of space so the function call is not at the entry point
int offset = (int) from.getEntryPoint().getOffset() + 5;
int toOffset = (int) to.getEntryPoint().getOffset();
while (!createReference(offset, toOffset)) {
offset++;
}
}
private Function ensureFunction(long from) throws Exception {
ProgramDB p = builder.getProgram(); ProgramDB p = builder.getProgram();
FunctionManager fm = p.getFunctionManager(); FunctionManager fm = p.getFunctionManager();
Function f = fm.getFunctionAt(addr(from)); Function f = fm.getFunctionAt(addr(from));
if (f != null) { if (f != null) {
return; return f;
} }
String a = Long.toHexString(from); String a = Long.toHexString(from);
builder.createEmptyFunction("Function_" + a, "0x" + a, 50, DataType.DEFAULT); return builder.createEmptyFunction("Function_" + a, "0x" + a, 50, DataType.DEFAULT);
} }
private boolean createReference(long from, long to) { private boolean createReference(long from, long to) {
@@ -580,12 +608,10 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
setProviderFunction("0x5000"); setProviderFunction("0x5000");
final ToggleDockingAction filterDuplicatesAction = ToggleDockingAction filterDuplicatesAction =
(ToggleDockingAction) getAction("Filter Duplicates"); (ToggleDockingAction) getAction("Filter Duplicates");
if (!filterDuplicatesAction.isSelected()) { setToggleActionSelected(filterDuplicatesAction, new ActionContext(), true);
performAction(filterDuplicatesAction, true);
}
waitForTree(outgoingTree); waitForTree(outgoingTree);
GTreeNode rootNode = getRootNode(outgoingTree); GTreeNode rootNode = getRootNode(outgoingTree);
@@ -597,8 +623,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
// copy the names of the children into a map so that we can verify that there are // copy the names of the children into a map so that we can verify that there are
// no duplicates // no duplicates
boolean shouldHaveDuplicates = false; boolean shouldHaveDuplicates = false;
Map<String, Integer> nameCountMap = createNameCountMap(rootNode); Map<GTreeNode, Integer> nameCountMap = createNameCountMap(rootNode);
System.out.println("nameCountMap: before: = " + nameCountMap);
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates); assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
performAction(filterDuplicatesAction, true);// deselect performAction(filterDuplicatesAction, true);// deselect
@@ -608,7 +633,47 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
rootNode = getRootNode(outgoingTree); rootNode = getRootNode(outgoingTree);
nameCountMap = createNameCountMap(rootNode); nameCountMap = createNameCountMap(rootNode);
shouldHaveDuplicates = true; shouldHaveDuplicates = true;
System.out.println("nameCountMap: after: = " + nameCountMap); assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
}
@Test
public void testFilterIncomingDuplicates_SameFunctionNameInDifferentNamespaces()
throws Exception {
/*
Create a function call tree that looks like:
NS1::Foo
root
NS2::Foo
root
*/
Function f1 = function("NS1", "Foo", 0x11000);
Function f2 = function("NS2", "Foo", 0x12000);
Function root = function(0x0000);
function(f1, root);
function(f2, root);
setProviderFunction("0x0000");
ToggleDockingAction filterDuplicatesAction =
(ToggleDockingAction) getAction("Filter Duplicates");
setToggleActionSelected(filterDuplicatesAction, new ActionContext(), true);
waitForTree(incomingTree);
GTreeNode rootNode = getRootNode(incomingTree);
List<GTreeNode> children = rootNode.getChildren();
assertTrue(
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
children.size() > 0);
// copy the names of the children into a map so that we can verify that there are
// no duplicates
boolean shouldHaveDuplicates = false;
Map<GTreeNode, Integer> nameCountMap = createNameCountMap(rootNode);
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates); assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
} }
@@ -1071,27 +1136,26 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
return codeBrowserPlugin.getCurrentAddress(); return codeBrowserPlugin.getCurrentAddress();
} }
private Map<String, Integer> createNameCountMap(GTreeNode node) { private Map<GTreeNode, Integer> createNameCountMap(GTreeNode node) {
Map<String, Integer> map = new HashMap<>(); Map<GTreeNode, Integer> map = new HashMap<>();
List<GTreeNode> children = node.getChildren(); List<GTreeNode> children = node.getChildren();
for (GTreeNode child : children) { for (GTreeNode child : children) {
String name = child.getName(); Integer integer = map.get(child);
Integer integer = map.get(name);
if (integer == null) { if (integer == null) {
integer = 0; integer = 0;
} }
int asInt = integer; int asInt = integer;
asInt++; asInt++;
map.put(name, asInt); map.put(child, asInt);
} }
return map; return map;
} }
private void assertDuplicateChildStatus(Map<String, Integer> childCountMap, private void assertDuplicateChildStatus(Map<GTreeNode, Integer> childCountMap,
boolean shouldHaveDuplicates) { boolean shouldHaveDuplicates) {
boolean foundDuplicates = false; boolean foundDuplicates = false;
Set<Entry<String, Integer>> entrySet = childCountMap.entrySet(); Set<Entry<GTreeNode, Integer>> entrySet = childCountMap.entrySet();
for (Entry<String, Integer> entry : entrySet) { for (Entry<GTreeNode, Integer> entry : entrySet) {
int value = entry.getValue(); int value = entry.getValue();
if (value != 1) { if (value != 1) {
foundDuplicates = true; foundDuplicates = true;