dap: add Globals display to debugger

This commit is contained in:
nikitalita 2025-03-10 02:37:53 -07:00
parent 3e163d6b1f
commit 9dbef641cd
5 changed files with 177 additions and 1 deletions

View file

@ -1190,6 +1190,7 @@ set (PCH_SOURCES
common/scripting/dap/IdProvider.cpp
common/scripting/dap/Nodes/ArrayStateNode.cpp
common/scripting/dap/Nodes/LocalScopeStateNode.cpp
common/scripting/dap/Nodes/GlobalScopeStateNode.cpp
common/scripting/dap/Nodes/ObjectStateNode.cpp
common/scripting/dap/Nodes/StackFrameStateNode.cpp
common/scripting/dap/Nodes/StackStateNode.cpp

View file

@ -0,0 +1,141 @@
#include "GlobalScopeStateNode.h"
#include <common/scripting/dap/Utilities.h>
#include <common/scripting/dap/RuntimeState.h>
namespace DebugServer
{
// TODO: Do this dynamically?
static const char *const GlobalNames[] = {
"NotifyFontScale",
"ConsoleState",
"menuactive",
"BackbuttonTime",
"BackbuttonAlpha",
"GameTicRate",
"menuDelegate",
"WP_NOCHANGE",
"SmallFont",
"SmallFont2",
"BigFont",
"ConFont",
"NewConsoleFont",
"NewSmallFont",
"AlternativeSmallFont",
"AlternativeBigFont",
"OriginalSmallFont",
"OriginalBigFont",
"IntermissionFont",
"CleanXfac",
"CleanYfac",
"CleanWidth",
"CleanHeight",
"CleanXfac_1",
"CleanYfac_1",
"CleanWidth_1",
"CleanHeight_1",
"AllServices",
"Bindings",
"AutomapBindings",
"generic_ui",
"deh",
"gameinfo",
"Teams",
"LocalViewPitch",
"StatusBar",
"players",
"playeringame",
"PlayerClasses",
"consoleplayer",
"validcount",
"multiplayer",
"gameaction",
"gamestate",
"skyflatnum",
"globalfreeze",
"gametic",
"demoplayback",
"automapactive",
"viewactive",
"Net_Arbitrator",
"netgame",
"paused",
"Terrains",
"OptionMenuSettings",
"musplaying",
"AllClasses",
"Level",
// "level", technically its own global, but only the VM one is accessible by the VM
"AllActorClasses",
nullptr};
GlobalScopeStateNode::GlobalScopeStateNode() { }
bool GlobalScopeStateNode::SerializeToProtocol(dap::Scope &scope)
{
scope.name = "Global";
scope.expensive = false;
scope.presentationHint = "globals";
scope.variablesReference = GetId();
std::vector<std::string> childNames;
GetChildNames(childNames);
scope.namedVariables = childNames.size();
scope.indexedVariables = 0;
return true;
}
bool GlobalScopeStateNode::GetChildNames(std::vector<std::string> &names)
{
for (int i = 0; GlobalNames[i] != nullptr; i++)
{
names.push_back(GlobalNames[i]);
}
return true;
}
bool GlobalScopeStateNode::GetChildNode(std::string name, std::shared_ptr<StateNodeBase> &node)
{
if (m_children.empty())
{
std::vector<std::string> childNames;
GetChildNames(childNames);
caseless_path_set childSet {childNames.begin(), childNames.end()};
for (auto ns : Namespaces.AllNamespaces)
{
auto symbolIter = ns->Symbols.GetIterator();
PSymbolTable::MapType::Pair *pair;
while (symbolIter.NextPair(pair))
{
if (childSet.find(pair->Key.GetChars()) != childSet.end())
{
std::string symname = pair->Key.GetChars();
PSymbol *val = pair->Value;
if (val->SymbolName == NAME_None)
{
continue;
}
PField *field = dyn_cast<PField>(val);
if (field)
{
field->Type;
// the offset is the address of the field
void *addr = (void *)(field->Offset);
VMValue val = GetVMValue(addr, field->Type);
m_children[symname] = RuntimeState::CreateNodeForVariable(symname, val, field->Type);
}
}
}
}
}
if (m_children.find(name) != m_children.end())
{
node = m_children[name];
return true;
}
return false;
}
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <common/scripting/dap/GameInterfaces.h>
#include <dap/protocol.h>
#include "StateNodeBase.h"
namespace DebugServer
{
class GlobalScopeStateNode : public StateNodeBase, public IProtocolScopeSerializable, public IStructuredState
{
caseless_path_map<std::shared_ptr<StateNodeBase>> m_children;
public:
GlobalScopeStateNode();
bool SerializeToProtocol(dap::Scope &scope) override;
bool GetChildNames(std::vector<std::string> &names) override;
bool GetChildNode(std::string name, std::shared_ptr<StateNodeBase> &node) override;
};
}

View file

@ -1,10 +1,12 @@
#include "StackFrameStateNode.h"
#include <common/scripting/dap/Utilities.h>
#include <string>
#include "LocalScopeStateNode.h"
#include "RegistersScopeStateNode.h"
#include "GlobalScopeStateNode.h"
namespace DebugServer
{
@ -20,6 +22,7 @@ StackFrameStateNode::StackFrameStateNode(VMFunction *nativeFunction, VMFrame *pa
m_fakeStackFrame.MaxParam = 0;
m_fakeStackFrame.NumParam = 0;
m_stackFrame = &m_fakeStackFrame;
m_globalsScope = std::make_shared<GlobalScopeStateNode>();
}
StackFrameStateNode::StackFrameStateNode(VMFrame *stackFrame) : m_stackFrame(stackFrame), m_fakeStackFrame()
@ -33,6 +36,7 @@ StackFrameStateNode::StackFrameStateNode(VMFrame *stackFrame) : m_stackFrame(sta
}
m_registersScope = std::make_shared<RegistersScopeStateNode>(m_stackFrame);
}
m_globalsScope = std::make_shared<GlobalScopeStateNode>();
}
bool StackFrameStateNode::SerializeToProtocol(dap::StackFrame &stackFrame, PexCache *pexCache) const
@ -86,10 +90,14 @@ bool StackFrameStateNode::SerializeToProtocol(dap::StackFrame &stackFrame, PexCa
bool StackFrameStateNode::GetChildNames(std::vector<std::string> &names)
{
auto scriptFunction = dynamic_cast<VMScriptFunction *>(m_stackFrame->Func);
auto scriptFunction = GetVMScriptFunction(m_stackFrame->Func);
if (scriptFunction)
{
names.push_back("Local");
}
names.push_back("Globals");
if (scriptFunction)
{
names.push_back("Registers");
}
return true;
@ -101,6 +109,11 @@ bool StackFrameStateNode::GetChildNode(std::string name, std::shared_ptr<StateNo
{
return false;
}
if (CaseInsensitiveEquals(name, "Globals"))
{
node = m_globalsScope;
return true;
}
if (CaseInsensitiveEquals(name, "Registers"))
{
node = m_registersScope;

View file

@ -15,6 +15,7 @@ class StackFrameStateNode : public StateNodeBase, public IStructuredState
VMFrame m_fakeStackFrame;
std::shared_ptr<StateNodeBase> m_localScope = nullptr;
std::shared_ptr<StateNodeBase> m_registersScope = nullptr;
std::shared_ptr<StateNodeBase> m_globalsScope = nullptr;
public:
StackFrameStateNode(VMFunction *nativeFunction, VMFrame *parentStackFrame);
explicit StackFrameStateNode(VMFrame *stackFrame);