GP-2223: Better strategy for computing module base with GDB.

This commit is contained in:
Dan 2023-08-14 14:37:46 -04:00
parent c32b6d6bdb
commit 215cd7c4f3
5 changed files with 59 additions and 16 deletions

View File

@ -16,8 +16,7 @@
package agent.gdb.manager;
import java.math.BigInteger;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import agent.gdb.manager.GdbManager.StepCmd;
@ -133,14 +132,14 @@ public interface GdbInferior extends GdbConsoleOperations, GdbMemoryOperations {
*
* @return a map of start addresses to mapped memory regions
*/
Map<BigInteger, GdbMemoryMapping> getKnownMappings();
NavigableMap<BigInteger, GdbMemoryMapping> getKnownMappings();
/**
* List the memory mappings of this inferior's process
*
* @return a future that completes with a map of start addresses to mapped memory regions
*/
CompletableFuture<Map<BigInteger, GdbMemoryMapping>> listMappings();
CompletableFuture<NavigableMap<BigInteger, GdbMemoryMapping>> listMappings();
/**
* Change CLI focus to this inferior

View File

@ -252,9 +252,12 @@ public class GdbInferiorImpl implements GdbInferior {
}
protected CompletableFuture<Map<String, GdbModule>> doListModules() {
return manager.execMaintInfoSectionsAllObjects(this).thenApply(lines -> {
parseAndUpdateAllModuleSections(lines);
return unmodifiableModules;
return manager.execMaintInfoSectionsAllObjects(this).thenCompose(lines -> {
return listMappings().thenApply(mappings -> {
GdbMemoryMapping.Index index = new GdbMemoryMapping.Index(mappings);
parseAndUpdateAllModuleSections(lines, index);
return unmodifiableModules;
});
});
}
@ -297,7 +300,7 @@ public class GdbInferiorImpl implements GdbInferior {
return name;
}
protected void parseAndUpdateAllModuleSections(String[] lines) {
protected void parseAndUpdateAllModuleSections(String[] lines, GdbMemoryMapping.Index index) {
Set<String> modNamesSeen = new HashSet<>();
Set<String> secNamesSeen = new HashSet<>();
GdbModuleImpl curModule = null;
@ -312,7 +315,7 @@ public class GdbInferiorImpl implements GdbInferior {
curModule = modules.computeIfAbsent(name, this::resyncCreateModule);
}
else if (curModule != null) {
curModule.processSectionLine(line, secNamesSeen);
curModule.processSectionLine(line, secNamesSeen, index);
}
}
if (curModule != null) {
@ -323,12 +326,12 @@ public class GdbInferiorImpl implements GdbInferior {
}
@Override
public Map<BigInteger, GdbMemoryMapping> getKnownMappings() {
public NavigableMap<BigInteger, GdbMemoryMapping> getKnownMappings() {
return unmodifiableMappings;
}
@Override
public CompletableFuture<Map<BigInteger, GdbMemoryMapping>> listMappings() {
public CompletableFuture<NavigableMap<BigInteger, GdbMemoryMapping>> listMappings() {
return consoleCapture("info proc mappings", CompletesWithRunning.CANNOT)
.thenApply(this::parseMappings);
}
@ -361,7 +364,7 @@ public class GdbInferiorImpl implements GdbInferior {
return new GdbMemoryMapping(start, end, size, offset, "rwx", objfile);
}
protected Map<BigInteger, GdbMemoryMapping> parseMappings(String out) {
protected NavigableMap<BigInteger, GdbMemoryMapping> parseMappings(String out) {
Set<BigInteger> startsSeen = new TreeSet<>();
for (String line : out.split("\n")) {
try {

View File

@ -16,12 +16,51 @@
package agent.gdb.manager.impl;
import java.math.BigInteger;
import java.util.Objects;
import java.util.*;
import java.util.Map.Entry;
/**
* The abstraction for a line of output from {@code info proc mappings}
*/
public class GdbMemoryMapping {
static class Index {
final NavigableMap<BigInteger, GdbMemoryMapping> mappings;
final Map<String, BigInteger> bases;
public Index(NavigableMap<BigInteger, GdbMemoryMapping> mappings) {
this.mappings = mappings;
this.bases = new HashMap<>();
for (GdbMemoryMapping mapping : mappings.values()) {
if (mapping.objfile == null || mapping.objfile.isBlank()) {
continue;
}
// Values should be ordered ascending by address
if (bases.containsKey(mapping.objfile)) {
continue;
}
bases.put(mapping.objfile, mapping.start);
}
}
public BigInteger computeBase(BigInteger vma) {
Entry<BigInteger, GdbMemoryMapping> floor = mappings.floorEntry(vma);
if (floor == null) {
return vma;
}
GdbMemoryMapping mapping = floor.getValue();
// NB. Ends given by GDB are exclusive
if (mapping.objfile.isBlank() || mapping.end.compareTo(vma) <= 0) {
return vma;
}
return Objects.requireNonNull(bases.get(mapping.objfile));
}
public long computeBase(long vma) {
return computeBase(BigInteger.valueOf(vma)).longValueExact();
}
}
private final BigInteger start;
private final BigInteger end;
private final BigInteger size;

View File

@ -150,7 +150,8 @@ public class GdbModuleImpl implements GdbModule {
return minimalSymbols.request();
}
protected void processSectionLine(String line, Set<String> namesSeen) {
protected void processSectionLine(String line, Set<String> namesSeen,
GdbMemoryMapping.Index index) {
Matcher matcher = inferior.manager.matchSectionLine(line);
if (matcher != null) {
try {
@ -167,7 +168,7 @@ public class GdbModuleImpl implements GdbModule {
}
}
if (attrs.contains("ALLOC")) {
long b = vmaStart - offset;
long b = index.computeBase(vmaStart);
base = base == null ? b : MathUtilities.unsignedMin(base, b);
max = max == null ? b : MathUtilities.unsignedMax(max, vmaEnd);
}

View File

@ -131,7 +131,8 @@ public class GdbModelTargetProcessMemory
}
return inferior.listMappings().exceptionally(ex -> {
Msg.error(this, "Could not list regions. Using default.");
return Map.of(); // empty map will be replaced with default
// empty map will be replaced with default
return new TreeMap<>();
}).thenAccept(this::updateUsingMappings);
}