Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Localsplus and details #4

Merged
merged 10 commits into from
Jan 14, 2021
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This debugger extension provides visualizations for Python objects and stacktrac

The goal of this project is to provide a similar debugging experience in WinDbg/CDB/NTSD as `already exists in GDB <https://wiki.python.org/moin/DebuggingWithGdb>`_.

Currently, the extension is tested against 32bit and 64bit builds of Python versions 2.7, 3.3, 3.4, 3.5, and 3.6.
Currently, the extension is tested against 32bit and 64bit builds of Python versions 2.7, 3.3, 3.4, 3.5, 3.6, 3.7 and 3.8.

Installation
============
Expand Down
21 changes: 21 additions & 0 deletions include/PyCellObject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include "PyObject.h"
#include <string>

namespace PyExt::Remote {

/// Represents a PyBoolObject in the debuggee's address space.
class PYEXT_PUBLIC PyCellObject : public PyObject
{

public: // Construction/Destruction.
explicit PyCellObject(Offset objectAddress);

public: // Members.
auto objectReference() const -> std::unique_ptr<PyObject>;
auto repr(bool pretty = true) const -> std::string override;

};

}
10 changes: 9 additions & 1 deletion include/PyCodeObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,22 @@ namespace PyExt::Remote {
public: // Construction/Destruction.
explicit PyCodeObject(Offset objectAddress);

public: // Members.
public:
// Members.
auto numberOfLocals() const -> int;
auto firstLineNumber() const -> int;
auto lineNumberFromInstructionOffset(int instruction) const -> int;
auto varNames() const->std::vector<std::string>;
auto freeVars() const->std::vector<std::string>;
auto cellVars() const->std::vector<std::string>;
auto filename() const -> std::string;
auto name() const -> std::string;
auto lineNumberTable() const -> std::vector<std::uint8_t>;
auto repr(bool pretty = true) const -> std::string override;

protected:
// Helpers.
auto readStringTuple(std::string name) const -> std::vector<std::string>;
};

}
2 changes: 2 additions & 0 deletions include/PyFrameObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace PyExt::Remote {

public: // Members.
auto locals() const -> std::unique_ptr<PyDictObject>;
auto localsplus() const -> std::vector<std::pair<std::string, std::unique_ptr<PyObject>>>;
auto globals() const -> std::unique_ptr<PyDictObject>;
auto builtins() const -> std::unique_ptr<PyDictObject>;
auto code() const -> std::unique_ptr<PyCodeObject>;
Expand All @@ -28,6 +29,7 @@ namespace PyExt::Remote {
auto lastInstruction() const -> int;
auto currentLineNumber() const -> int;
auto repr(bool pretty = true) const -> std::string override;
auto details() const -> std::string override;

};

Expand Down
44 changes: 44 additions & 0 deletions include/PyMemberDef.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#include "RemoteType.h"
#include "pyextpublic.h"

namespace PyExt::Remote {

class PYEXT_PUBLIC PyMemberDef : private RemoteType
{

public: // Constants.
static const int T_SHORT = 0;
static const int T_INT = 1;
static const int T_LONG = 2;
static const int T_FLOAT = 3;
static const int T_DOUBLE = 4;
static const int T_STRING = 5;
static const int T_OBJECT = 6;
static const int T_CHAR = 7;
static const int T_BYTE = 8;
static const int T_UBYTE = 9;
static const int T_USHORT = 10;
static const int T_UINT = 11;
static const int T_ULONG = 12;
static const int T_STRING_INPLACE = 13;
static const int T_BOOL = 14;
static const int T_OBJECT_EX = 16;
static const int T_LONGLONG = 17;
static const int T_ULONGLONG = 18;
static const int T_PYSSIZET = 19;
static const int T_NONE = 20;

public: // Construction/Destruction.
explicit PyMemberDef(Offset objectAddress);
~PyMemberDef();

public: // Members of the remote type.
auto name() const -> std::string;
auto type() const -> int;
auto offset() const -> SSize;

};

}
11 changes: 9 additions & 2 deletions include/PyObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,35 @@ class ExtRemoteTyped;
namespace PyExt::Remote {

class PyTypeObject; //< Forward Declaration.
class PyDictObject; //< Forward Declaration.

/// Represents a PyObject in the debuggee's address space. Base class for all types of PyObject.
class PYEXT_PUBLIC PyObject : private RemoteType
{

public: // Typedefs.
using RemoteType::Offset;
using SSize = std::int64_t;
using RemoteType::SSize;

public: // Construction/Destruction.
explicit PyObject(Offset objectAddress, const std::string& symbolName = "PyObject");
virtual ~PyObject();

/// Polymorphic constructor. Creates the most-derived PyObject it can.
// Polymorphic constructor. Creates the most-derived PyObject it can.
static auto make(PyObject::Offset remoteAddress) -> std::unique_ptr<PyObject>;
// Constructor by type name. Necessary to get the base type repr for types derived from built-in types.
static auto make(PyObject::Offset remoteAddress, const std::string& typeName) -> std::unique_ptr<PyObject>;
using RemoteType::readOffsetArray;

public: // Members.
using RemoteType::offset;
using RemoteType::symbolName;
auto refCount() const -> SSize;
auto type() const -> PyTypeObject;
auto slots() const -> std::vector<std::pair<std::string, std::unique_ptr<PyObject>>>;
auto dict() const -> std::unique_ptr<PyDictObject>;
virtual auto repr(bool pretty = true) const -> std::string;
virtual auto details() const -> std::string;

protected: // Helpers for more derived classes.
/// Returns a field by name in the `ob_base` member.
Expand Down
3 changes: 2 additions & 1 deletion include/PyStringObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace PyExt::Remote {
public: // Members.
auto stringLength() const -> SSize;
auto stringValue() const -> std::string override;
auto repr(bool pretty = true) const -> std::string override;
virtual auto repr(bool pretty = true) const -> std::string override;

};

Expand All @@ -27,6 +27,7 @@ namespace PyExt::Remote {
class PYEXT_PUBLIC PyBytesObject : public PyBaseStringObject {
public:
explicit PyBytesObject(Offset objectAddress);
auto repr(bool pretty = true) const -> std::string override;
};


Expand Down
29 changes: 29 additions & 0 deletions include/PyTypeObject.h
Original file line number Diff line number Diff line change
@@ -1,20 +1,49 @@
#pragma once

#include "PyVarObject.h"
#include "PyMemberDef.h"
#include "PyTupleObject.h"
#include <array>
#include <string>

namespace PyExt::Remote {

/// Represents a PyTypeObject in the debuggee's address space.
class PYEXT_PUBLIC PyTypeObject : public PyVarObject
{
public:
static const inline std::array builtinTypes{
"type",
"str",
"bytes",
"bytearray",
"tuple",
"set",
"dict",
"int",
"long",
"float",
"bool",
"complex",
"frame",
"code",
"function",
"cell",
"NoneType",
"NotImplementedType",
};

public: // Construction/Destruction.
explicit PyTypeObject(Offset objectAddress);

public: // Members.
auto name() const -> std::string;
auto basicSize() const -> SSize;
auto itemSize() const->SSize;
auto documentation() const -> std::string;
auto members() const -> std::vector<std::unique_ptr<PyMemberDef>>;
auto dictOffset() const -> SSize;
auto mro() const -> std::unique_ptr<PyTupleObject>;
auto isPython2() const -> bool;
auto repr(bool pretty = true) const -> std::string override;

Expand Down
3 changes: 3 additions & 0 deletions include/RemoteType.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace PyExt::Remote {

public: // Typedefs.
using Offset = std::uint64_t;
using SSize = std::int64_t;

public: // Construction/Destruction.
explicit RemoteType(Offset objectAddress, const std::string& symbolName);
Expand All @@ -31,6 +32,8 @@ namespace PyExt::Remote {
public: // Methods.
auto offset() const -> Offset;
auto symbolName() const -> std::string;
// necessary for x86 because offsets in remote address space are only 32 Bit
static auto readOffsetArray(/*const*/ ExtRemoteTyped& remoteArray, unsigned long numElements) -> std::vector<Offset>;

protected: // Helpers for more derived classes.
/// Access to the instance's memory in the debuggee.
Expand Down
2 changes: 1 addition & 1 deletion include/utils/ScopeExit.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace utils {
other.reset();
};

ScopeExit& operator=(ScopeExit&&)
ScopeExit& operator=(ScopeExit&& other)
{
f_ = std::move(other.f_);
other.reset();
Expand Down
46 changes: 46 additions & 0 deletions src/ExtHelpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "ExtHelpers.h"

#include <string>
#include <sstream>
using namespace std;


namespace utils {

auto getPointerSize() -> uint64_t
{
string objExpression = "sizeof(void*)"s;
ExtRemoteTyped remoteObj(objExpression.c_str());
return utils::readIntegral<uint64_t>(remoteObj);
}


auto escapeDml(const string& str) -> string
{
std::string buffer;
buffer.reserve(str.size());
for (auto ch : str) {
switch (ch) {
case '&': buffer += "&amp;"; break;
case '\"': buffer += "&quot;"; break;
// case '\'': buffer += "&apos;"; break; no DML special character?!
case '<': buffer += "&lt;"; break;
case '>': buffer += "&gt;"; break;
default: buffer += ch; break;
}
}
return buffer;
}


auto link(const string& text, const string& cmd, const string& alt) -> string
{
ostringstream oss;
oss << "<link cmd=\"" << escapeDml(cmd) << "\"";
if (!alt.empty())
oss << " alt=\"" << escapeDml(alt) << "\"";
oss << ">" << escapeDml(text) << "</link>";
return oss.str();
}

}
14 changes: 10 additions & 4 deletions src/ExtHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <engextcpp.hpp>
#include <type_traits>
#include <vector>
using namespace std::literals::string_literals;

namespace utils {

Expand All @@ -14,13 +15,13 @@ namespace utils {
auto size = remoteData.GetTypeSize();
switch (size) {
case 1:
return static_cast<Integral>(isSigned ? remoteData.GetChar() : remoteData.GetUchar());
return isSigned ? static_cast<Integral>(remoteData.GetChar()) : static_cast<Integral>(remoteData.GetUchar());
case 2:
return static_cast<Integral>(isSigned ? remoteData.GetShort() : remoteData.GetUshort());
return isSigned ? static_cast<Integral>(remoteData.GetShort()) : static_cast<Integral>(remoteData.GetUshort());
case 4:
return static_cast<Integral>(isSigned ? remoteData.GetLong() : remoteData.GetUlong());
return isSigned ? static_cast<Integral>(remoteData.GetLong()) : static_cast<Integral>(remoteData.GetUlong());
case 8:
return static_cast<Integral>(isSigned ? remoteData.GetLong64() : remoteData.GetUlong64());
return isSigned ? static_cast<Integral>(remoteData.GetLong64()) : static_cast<Integral>(remoteData.GetUlong64());
}

g_Ext->ThrowInterrupt();
Expand All @@ -42,4 +43,9 @@ namespace utils {
remoteData.ReadBuffer(buffer.data(), numElements*remoteSize);
return buffer;
}

auto getPointerSize() -> std::uint64_t;
auto escapeDml(const std::string& str) -> std::string;
auto link(const std::string& text, const std::string& cmd, const std::string& alt = ""s) -> std::string;

}
11 changes: 8 additions & 3 deletions src/PyExt.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{EEBC73BA-27F2-4483-8639-54A108B77594}</ProjectGuid>
<RootNamespace>PyExt</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
Expand All @@ -32,7 +32,7 @@
</PropertyGroup>
<PropertyGroup>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
Expand Down Expand Up @@ -62,7 +62,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard>stdcpp17</LanguageStandard>
<PreprocessorDefinitions>PYEXT_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
Expand Down Expand Up @@ -92,6 +92,8 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="extension.cpp" />
<ClCompile Include="ExtHelpers.cpp" />
<ClCompile Include="objects\PyCellObject.cpp" />
<ClCompile Include="objects\PySetObject.cpp" />
<ClCompile Include="objects\PyStringValue.cpp" />
<ClCompile Include="objects\PyBoolObject.cpp" />
Expand Down Expand Up @@ -119,6 +121,7 @@
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="PyInterpreterState.cpp" />
<ClCompile Include="PyMemberDef.cpp" />
<ClCompile Include="pystack.cpp" />
<ClCompile Include="pysymfix.cpp" />
<ClCompile Include="PyThreadState.cpp" />
Expand All @@ -136,7 +139,9 @@
<ResourceCompile Include="$(ProjectName).rc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\PyCellObject.h" />
<ClInclude Include="..\include\PyInterpreterState.h" />
<ClInclude Include="..\include\PyMemberDef.h" />
<ClInclude Include="..\include\PySetObject.h" />
<ClInclude Include="..\include\PyThreadState.h" />
<ClInclude Include="..\include\RemoteType.h" />
Expand Down
Loading