Skip to content

Commit be6c547

Browse files
Copilotxusheng6
andcommitted
Implement GetModuleList() for GDB MI adapter
Co-authored-by: xusheng6 <94503187+xusheng6@users.noreply.github.com>
1 parent ce9b09e commit be6c547

1 file changed

Lines changed: 156 additions & 0 deletions

File tree

core/adapters/gdbmiadapter.cpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "gdbmiadapter.h"
22
#include <regex>
3+
#include <sstream>
4+
#include <map>
35
#include "../debuggercontroller.h" // Assuming this path is correct for your project structure
46
#include "../../cli/log.h" // For Log::print
57

@@ -774,7 +776,161 @@ std::vector<DebugModule> GdbMiAdapter::GetModuleList()
774776
if (!m_mi)
775777
return {};
776778

779+
// Use -interpreter-exec to run the console command "info proc mappings"
780+
auto result = m_mi->SendCommand("-interpreter-exec console \"info proc mappings\"");
781+
if (result.command != "done")
782+
{
783+
LogWarn("Failed to get process mappings: %s", result.fullLine.c_str());
784+
return {};
785+
}
786+
777787
std::vector<DebugModule> modules;
788+
std::map<std::string, int> moduleNameCount; // Track module name occurrences for duplicates
789+
std::map<std::string, std::vector<std::pair<uint64_t, uint64_t>>> moduleRanges; // path -> list of (start, end)
790+
std::vector<std::string> moduleOrder; // Track the order in which modules are first seen
791+
792+
// Parse the console output from async records
793+
// The output will be in console stream records ('~')
794+
// We need to accumulate the console output and parse it
795+
// For now, we'll try to parse from the result payload if available
796+
797+
// Since the output is sent as console stream, we need a different approach.
798+
// Let's send the command and wait for console output.
799+
// Actually, the console output should be in the async records.
800+
// For simplicity, let's use InvokeBackendCommand which also uses -interpreter-exec
801+
std::string output = InvokeBackendCommand("info proc mappings");
802+
803+
if (output.empty() || output == "error, transport not ready")
804+
{
805+
LogWarn("Failed to get process mappings output");
806+
return {};
807+
}
808+
809+
// Parse the output line by line
810+
// Expected format (from the issue):
811+
// process 25443
812+
// Mapped address spaces:
813+
//
814+
// Start Addr End Addr Size Offset Perms objfile
815+
// 0x555555554000 0x555555555000 0x1000 0x0 r--p /path/to/file
816+
817+
std::istringstream stream(output);
818+
std::string line;
819+
bool headerFound = false;
820+
821+
while (std::getline(stream, line))
822+
{
823+
// Skip until we find the header line
824+
if (!headerFound)
825+
{
826+
if (line.find("Start Addr") != std::string::npos &&
827+
line.find("End Addr") != std::string::npos)
828+
{
829+
headerFound = true;
830+
}
831+
continue;
832+
}
833+
834+
// Parse data lines
835+
// Format: Start_Addr End_Addr Size Offset Perms objfile
836+
std::istringstream lineStream(line);
837+
std::string startStr, endStr, sizeStr, offsetStr, perms, objfile;
838+
839+
lineStream >> startStr >> endStr >> sizeStr >> offsetStr >> perms;
840+
841+
// Rest of the line is the objfile (path)
842+
std::getline(lineStream, objfile);
843+
844+
// Trim leading whitespace from objfile
845+
size_t firstNonSpace = objfile.find_first_not_of(" \t");
846+
if (firstNonSpace != std::string::npos)
847+
{
848+
objfile = objfile.substr(firstNonSpace);
849+
}
850+
851+
// Skip lines without valid addresses or without objfile
852+
if (startStr.empty() || endStr.empty() || objfile.empty())
853+
continue;
854+
855+
// Skip special mappings like [stack], [heap], [vvar], [vdso], etc.
856+
if (objfile[0] == '[')
857+
continue;
858+
859+
try
860+
{
861+
uint64_t start = std::stoull(startStr, nullptr, 16);
862+
uint64_t end = std::stoull(endStr, nullptr, 16);
863+
864+
// Track the order of first occurrence
865+
if (moduleRanges.find(objfile) == moduleRanges.end())
866+
{
867+
moduleOrder.push_back(objfile);
868+
}
869+
870+
// Accumulate ranges for each object file
871+
moduleRanges[objfile].emplace_back(start, end);
872+
}
873+
catch (const std::exception& e)
874+
{
875+
LogDebug("Failed to parse address range: %s", e.what());
876+
continue;
877+
}
878+
}
879+
880+
// Now create DebugModule entries in the order they were first encountered
881+
// For each unique object file, we need to determine its overall address range
882+
for (const auto& path : moduleOrder)
883+
{
884+
const auto& ranges = moduleRanges[path];
885+
if (ranges.empty())
886+
continue;
887+
888+
// Find the minimum start and maximum end
889+
uint64_t minStart = ranges[0].first;
890+
uint64_t maxEnd = ranges[0].second;
891+
892+
for (const auto& [start, end] : ranges)
893+
{
894+
minStart = std::min(minStart, start);
895+
maxEnd = std::max(maxEnd, end);
896+
}
897+
898+
// Extract the base name from the path for m_short_name
899+
std::string shortName = path;
900+
size_t lastSlash = path.find_last_of("/\\");
901+
if (lastSlash != std::string::npos)
902+
{
903+
shortName = path.substr(lastSlash + 1);
904+
}
905+
906+
// Handle duplicate names by appending -1, -2, etc.
907+
// The first occurrence gets the original name, subsequent ones get -1, -2, etc.
908+
std::string finalName = path;
909+
if (moduleNameCount.find(path) != moduleNameCount.end())
910+
{
911+
// This is a duplicate (shouldn't happen with current logic, but keep for safety)
912+
int count = ++moduleNameCount[path];
913+
finalName = path + "-" + std::to_string(count);
914+
}
915+
else
916+
{
917+
// First occurrence
918+
moduleNameCount[path] = 0;
919+
}
920+
921+
DebugModule module;
922+
module.m_name = finalName;
923+
module.m_short_name = shortName;
924+
module.m_address = minStart;
925+
module.m_size = maxEnd - minStart;
926+
module.m_loaded = true;
927+
928+
modules.push_back(module);
929+
}
930+
931+
return modules;
932+
}
933+
778934
return modules;
779935
}
780936

0 commit comments

Comments
 (0)