Rollup of all of the mixed mode changes (#8071)

* Support mixed mode for 3.10 (#8050)

* Fix 3.10 thread state

* Add the other versions elsewhere

* Test with native calling python

* Fix 3.10 thread state

* Add the other versions elsewhere

* Test with native calling python

* Add commit where _cframe was created

* Use an environment variable for python install path

* Get 3.11 to work in mixed mode (#8056)

* Fix 3.10 thread state

* Partially working for 3.11

Locals/Globals not being read correctly. They moved

* Basics for 3.11 working

* Fix stuff broken in 3.10 by 3.11 changes

* Fix tabs

* Fix review comments

* Support for 312 mixed mode debugging (#8063)

* Fix 3.10 thread state

* Partially working for 3.11

Locals/Globals not being read correctly. They moved

* Basics for 3.11 working

* Fix stuff broken in 3.10 by 3.11 changes

* Fix tabs

* Get python breakpoints working again

* Partially working for 3.11

Locals/Globals not being read correctly. They moved

* Get python breakpoints working again

* Get longs to work using new 3.12 long implementation

* Compute dynamic parts of the frame like the f_back and the line number

* Update comment

* Add support for mixed mode debugging in 3.13 (#8070)

* Changes for 3.13 Mixed mode debugging

* Fix step out/over

* Fix example to not hold file handle
This commit is contained in:
Rich Chiodo 2024-11-18 09:50:12 -08:00 коммит произвёл GitHub
Родитель 1c06c37c64
Коммит 8b1d41a17a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
40 изменённых файлов: 2104 добавлений и 570 удалений

Просмотреть файл

@ -1,6 +1,12 @@
import ctypes
def returnATuple():
return (4, 5)
def returnADict():
return { "4": 5 }
# Load the DLL
mylib = ctypes.CDLL('..\\x64\\Debug\\CppDll.dll')
@ -9,4 +15,9 @@ result = mylib.add(5, 10)
print(f"Result from C++ DLL: {result}")
print("After result is printed")
print("After result is printed")
x = returnATuple()
y = returnADict()
print("After tuple")

Просмотреть файл

@ -11,7 +11,7 @@
<OutputPath>.</OutputPath>
<Name>PythonApplication</Name>
<RootNamespace>PythonApplication</RootNamespace>
<InterpreterId>Global|PythonCore|3.9</InterpreterId>
<InterpreterId>Global|PythonCore|3.13</InterpreterId>
<LaunchProvider>Standard Python launcher</LaunchProvider>
<EnableNativeCodeDebugging>True</EnableNativeCodeDebugging>
</PropertyGroup>
@ -34,6 +34,10 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<InterpreterReference Include="Global|PythonCore|3.10" />
<InterpreterReference Include="Global|PythonCore|3.11" />
<InterpreterReference Include="Global|PythonCore|3.12" />
<InterpreterReference Include="Global|PythonCore|3.13" />
<InterpreterReference Include="Global|PythonCore|3.9" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" />

Просмотреть файл

@ -13,12 +13,13 @@ DWORD WINAPI runner(LPVOID lpParam)
std::filesystem::path cwd = std::filesystem::current_path();
std::filesystem::path startFile = cwd / "runner.py";
const char * startFileStr = startFile.string().c_str();
std::string str = startFile.string();
const char * startFileStr = str.c_str();
PyObject* startObj = Py_BuildValue("s", startFileStr);
FILE* file = _Py_fopen_obj(startObj, "rb+");
if (file != NULL) {
PyRun_SimpleFile(file, startFileStr);
PyRun_SimpleFileEx(file, startFileStr, 1);
}
Py_Finalize();

Просмотреть файл

@ -104,14 +104,14 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>C:\Users\rchiodo\AppData\Local\Programs\Python\Python311\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(LOCALAPPDATA)\Programs\Python\Python313\include;$(LOCALAPPDATA)\Programs\Python\Python312\include;$(LOCALAPPDATA)\Programs\Python\Python311\include;$(LOCALAPPDATA)\Programs\Python\Python310\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>C:\Users\rchiodo\AppData\Local\Programs\Python\Python311\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(LOCALAPPDATA)\Programs\Python\Python313\libs;$(LOCALAPPDATA)\Programs\Python\Python312\libs;$(LOCALAPPDATA)\Programs\Python\Python311\libs;$(LOCALAPPDATA)\Programs\Python\Python310\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -122,7 +122,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>C:\Users\rchiodo\AppData\Local\Programs\Python\Python311\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(LOCALAPPDATA)\Programs\Python\Python310\include;$(LOCALAPPDATA)\Programs\Python\Python311\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard_C>stdc17</LanguageStandard_C>
</ClCompile>
@ -131,7 +131,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>C:\Users\rchiodo\AppData\Local\Programs\Python\Python311\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(LOCALAPPDATA)\Programs\Python\Python310\libs;$(LOCALAPPDATA)\Programs\Python\Python311\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

Просмотреть файл

@ -39,7 +39,10 @@ namespace Microsoft.CookiecutterTools.Interpreters {
V37 = 0x0307,
V38 = 0x0308,
V39 = 0x0309,
V310 = 0x0310
V310 = 0x030a,
V311 = 0x030b,
V312 = 0x030c,
V313 = 0x030d,
}
public static class PythonLanguageVersionExtensions {
@ -87,6 +90,9 @@ namespace Microsoft.CookiecutterTools.Interpreters {
case 8: return PythonLanguageVersion.V38;
case 9: return PythonLanguageVersion.V39;
case 10: return PythonLanguageVersion.V310;
case 11: return PythonLanguageVersion.V311;
case 12: return PythonLanguageVersion.V312;
case 13: return PythonLanguageVersion.V313;
}
break;
}

Просмотреть файл

@ -93,7 +93,7 @@ namespace Microsoft.PythonTools.Debugger.Concord {
PyCodeObject code = pythonFrame.f_code.Read();
var loc = new SourceLocation(
code.co_filename.Read().ToStringOrNull(),
pythonFrame.f_lineno.Read(),
pythonFrame.ComputeLineNumber(stackContext.InspectionSession, nativeFrame, stackContext.FormatOptions.EvaluationFlags),
code.co_name.Read().ToStringOrNull(),
nativeFrame.InstructionAddress as DkmNativeInstructionAddress);

Просмотреть файл

@ -32,7 +32,10 @@ namespace Microsoft.PythonTools.Debugger.Concord {
private readonly DkmStackWalkFrame _nativeFrame;
private readonly DkmInspectionContext _cppInspectionContext;
public CppExpressionEvaluator(DkmInspectionContext inspectionContext, DkmStackWalkFrame stackFrame) {
public CppExpressionEvaluator(DkmInspectionContext inspectionContext, DkmStackWalkFrame stackFrame)
: this(inspectionContext.InspectionSession, inspectionContext.Radix, stackFrame) {
}
public CppExpressionEvaluator(DkmInspectionSession inspectionSession, uint radix, DkmStackWalkFrame stackFrame, DkmEvaluationFlags flags = DkmEvaluationFlags.TreatAsExpression | DkmEvaluationFlags.NoSideEffects) {
_process = stackFrame.Process;
var thread = stackFrame.Thread;
@ -53,16 +56,15 @@ namespace Microsoft.PythonTools.Debugger.Concord {
DkmStackWalkFrameFlags.None, null, stackFrame.Registers, null);
}
_cppInspectionContext = DkmInspectionContext.Create(inspectionContext.InspectionSession, _process.GetNativeRuntimeInstance(), thread, Timeout,
DkmEvaluationFlags.TreatAsExpression | DkmEvaluationFlags.NoSideEffects, DkmFuncEvalFlags.None, inspectionContext.Radix, CppLanguage, null);
_cppInspectionContext = DkmInspectionContext.Create(inspectionSession, _process.GetNativeRuntimeInstance(), thread, Timeout,
flags, DkmFuncEvalFlags.None, radix, CppLanguage, null);
}
public CppExpressionEvaluator(DkmThread thread, ulong frameBase, ulong vframe) {
public CppExpressionEvaluator(DkmThread thread, ulong frameBase, ulong vframe, DkmEvaluationFlags flags = DkmEvaluationFlags.TreatAsExpression | DkmEvaluationFlags.NoSideEffects) {
_process = thread.Process;
var inspectionSession = DkmInspectionSession.Create(_process, null);
_cppInspectionContext = DkmInspectionContext.Create(inspectionSession, _process.GetNativeRuntimeInstance(), thread, Timeout,
DkmEvaluationFlags.TreatAsExpression | DkmEvaluationFlags.NoSideEffects, DkmFuncEvalFlags.None, 10, CppLanguage, null);
flags, DkmFuncEvalFlags.None, 10, CppLanguage, null);
const int CV_ALLREG_VFRAME = 0x00007536;
var vframeReg = DkmUnwoundRegister.Create(CV_ALLREG_VFRAME, new ReadOnlyCollection<byte>(BitConverter.GetBytes(vframe)));
@ -79,8 +81,8 @@ namespace Microsoft.PythonTools.Debugger.Concord {
return expr;
}
public DkmEvaluationResult TryEvaluate(string expr) {
using (var cppExpr = DkmLanguageExpression.Create(CppLanguage, DkmEvaluationFlags.NoSideEffects, expr, null)) {
public DkmEvaluationResult TryEvaluate(string expr, DkmEvaluationFlags flags = DkmEvaluationFlags.NoSideEffects) {
using (var cppExpr = DkmLanguageExpression.Create(CppLanguage, flags, expr, null)) {
DkmEvaluationResult cppEvalResult = null;
var cppWorkList = DkmWorkList.Create(null);
_cppInspectionContext.EvaluateExpression(cppWorkList, cppExpr, _nativeFrame, (result) => {
@ -95,8 +97,8 @@ namespace Microsoft.PythonTools.Debugger.Concord {
return TryEvaluate(GetExpressionForObject(moduleName, typeName, address, tail));
}
public string Evaluate(string expr) {
var er = TryEvaluate(expr);
public string Evaluate(string expr, DkmEvaluationFlags flags = DkmEvaluationFlags.NoSideEffects) {
var er = TryEvaluate(expr, flags);
var ser = er as DkmSuccessEvaluationResult;
if (ser == null) {
throw new CppEvaluationException(er);
@ -104,9 +106,9 @@ namespace Microsoft.PythonTools.Debugger.Concord {
return ser.Value;
}
public int EvaluateInt32(string expr) {
public int EvaluateInt32(string expr, DkmEvaluationFlags flags = DkmEvaluationFlags.NoSideEffects) {
try {
return int.Parse(Evaluate("(__int32)(" + expr + ")"));
return int.Parse(Evaluate("(__int32)(" + expr + ")", flags));
} catch (FormatException) {
throw new CppEvaluationException();
}

Просмотреть файл

@ -107,6 +107,16 @@
<Compile Include="Proxies\DataProxy.cs" />
<Compile Include="ExpressionEvaluator.cs" />
<Compile Include="LocalStackWalkingComponent.cs" />
<Compile Include="Proxies\Structs\CFrameProxy.cs" />
<Compile Include="Proxies\Structs\ImportState.cs" />
<Compile Include="Proxies\Structs\PyCodeObject310.cs" />
<Compile Include="Proxies\Structs\PyCodeObject311.cs" />
<Compile Include="Proxies\Structs\PyDictObject.cs" />
<Compile Include="Proxies\Structs\PyDictObject311.cs" />
<Compile Include="Proxies\Structs\PyFrameObject310.cs" />
<Compile Include="Proxies\Structs\PyFrameObject311.cs" />
<Compile Include="Proxies\Structs\PyFunctionObject.cs" />
<Compile Include="Proxies\Structs\PyInterpreterFrame.cs" />
<Compile Include="Proxies\Structs\PyRuntimeState.cs" />
<Compile Include="Proxies\Structs\PyEllipsisObject.cs" />
<Compile Include="Proxies\Structs\PyComplexObject.cs" />
@ -118,6 +128,9 @@
<Compile Include="Proxies\Structs\PyBoolObject.cs" />
<Compile Include="Proxies\Structs\PySetObject.cs" />
<Compile Include="Proxies\Structs\PyCellObject.cs" />
<Compile Include="Proxies\Structs\PyThreads.cs" />
<Compile Include="Proxies\Structs\PyUnicodeObject311.cs" />
<Compile Include="Proxies\Structs\PyUnicodeObject312.cs" />
<Compile Include="PythonRuntimeInfo.cs" />
<Compile Include="StackFrameDataItem.cs" />
<Compile Include="ValueStore.cs" />
@ -138,7 +151,7 @@
<Compile Include="Proxies\Structs\PyInterpreterState.cs" />
<Compile Include="Proxies\StructProxy.cs" />
<Compile Include="Proxies\Structs\PyCodeObject.cs" />
<Compile Include="Proxies\Structs\PyDictObject.cs" />
<Compile Include="Proxies\Structs\PyDictObject310.cs" />
<Compile Include="Proxies\Structs\PyFrameObject.cs" />
<Compile Include="Proxies\Structs\PyListObject.cs" />
<Compile Include="Proxies\Structs\PyLongObject.cs" />

Просмотреть файл

@ -20,6 +20,9 @@ using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord {
public static class DebuggerOptions {
// These are intentionally not implemented as auto-properties to enable easily changing them at runtime, including when stopped in native code.
//
// These show up in commands in the debugger watch window if you set the registry value:
// Key: HKCU/Software/Microsoft/PythonTools/Debugger - Value: PythonDeveloper: DWORD = 1
private static bool _showNativePythonFrames;
private static bool _usePythonStepping;
private static bool _showCppViewNodes;
@ -29,7 +32,7 @@ namespace Microsoft.PythonTools.Debugger.Concord {
public static bool ShowNativePythonFrames {
get {
return _showNativePythonFrames; // Enable this to show native (C++) frames including our trace helper
return _showNativePythonFrames; // Enable this to show native (C++) frames including our trace helper and CPython code
}
set {
_showNativePythonFrames = value;
@ -39,7 +42,7 @@ namespace Microsoft.PythonTools.Debugger.Concord {
public static bool UsePythonStepping {
get {
return _usePythonStepping; // Disable this to step through the TraceHelper dll in the launched VS
return _usePythonStepping; // Disable this to step through the TraceHelper dll (or CPython) in the launched VS
}
set {
_usePythonStepping = value;

Просмотреть файл

@ -76,9 +76,14 @@ namespace Microsoft.PythonTools.Debugger.Concord {
_pyrtInfo = process.GetPythonRuntimeInfo();
LoadInitialPythonModules();
string pyCodeFunctionName = (_pyrtInfo.LanguageVersion < PythonLanguageVersion.V38) ? "PyCode_New" : "PyCode_NewWithPosOnlyArgs";
LocalComponent.CreateRuntimeDllFunctionBreakpoint(_pyrtInfo.DLLs.Python, pyCodeFunctionName, PythonDllBreakpointHandlers.PyCode_New, enable: true, debugStart: true);
LocalComponent.CreateRuntimeDllFunctionBreakpoint(_pyrtInfo.DLLs.Python, "PyCode_NewEmpty", PythonDllBreakpointHandlers.PyCode_NewEmpty, enable: true, debugStart: true);
if (_pyrtInfo.LanguageVersion <= PythonLanguageVersion.V310) {
LocalComponent.CreateRuntimeDllFunctionBreakpoint(_pyrtInfo.DLLs.Python, "PyCode_NewWithPosOnlyArgs", PythonDllBreakpointHandlers.PyCode_New, enable: true, debugStart: true);
LocalComponent.CreateRuntimeDllFunctionBreakpoint(_pyrtInfo.DLLs.Python, "PyCode_NewEmpty", PythonDllBreakpointHandlers.PyCode_NewEmpty, enable: true, debugStart: true);
} else {
// In 3.11, the PyCode_New functions were no longer used. Instead, an internal _PyCode_New function is used to create a code object.
LocalComponent.CreateRuntimeDllFunctionBreakpoint(_pyrtInfo.DLLs.Python, "_PyCode_New", PythonDllBreakpointHandlers._PyCode_New, enable: true, debugStart: true);
}
}
private void LoadInitialPythonModules() {
@ -160,6 +165,27 @@ namespace Microsoft.PythonTools.Debugger.Concord {
}.SendLower(process);
}
public static void _PyCode_New(DkmThread thread, ulong frameBase, ulong vframe, ulong returnAddress) {
var process = thread.Process;
var cppEval = new CppExpressionEvaluator(thread, frameBase, vframe);
var filenamePtr = cppEval.EvaluateUInt64("con->filename");
var filenameObj = PyObject.FromAddress(process, filenamePtr) as IPyBaseStringObject;
if (filenameObj == null) {
return;
}
string filename = filenameObj.ToString();
if (process.GetPythonRuntimeInstance().GetModuleInstances().Any(mi => mi.FullName == filename)) {
return;
}
new RemoteComponent.CreateModuleRequest {
ModuleId = Guid.NewGuid(),
FileName = filename
}.SendLower(process);
}
public static void PyCode_NewEmpty(DkmThread thread, ulong frameBase, ulong vframe, ulong returnAddress) {
var process = thread.Process;
var cppEval = new CppExpressionEvaluator(thread, frameBase, vframe);

Просмотреть файл

@ -58,21 +58,24 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies {
public static readonly FactoryFunc Factory;
static FactoryBuilder() {
FactoryFunc nonPolymorphicFactory;
var ctor = typeof(TProxy).GetConstructor(new[] { typeof(DkmProcess), typeof(ulong) });
if (ctor != null) {
var processParam = Expression.Parameter(typeof(DkmProcess));
var addressParam = Expression.Parameter(typeof(ulong));
var polymorphicParam = Expression.Parameter(typeof(bool));
nonPolymorphicFactory = Expression.Lambda<FactoryFunc>(
Expression.New(ctor, processParam, addressParam),
new[] { processParam, addressParam, polymorphicParam })
.Compile();
} else {
nonPolymorphicFactory = (process, address, polymorphic) => {
Debug.Fail("IDebuggeeReference-derived type " + typeof(TProxy).Name + " does not have a (DkmProcess, ulong) constructor.");
throw new NotSupportedException();
};
FactoryFunc nonPolymorphicFactory = (process, address, polymorphic) => {
Debug.Fail("IDebuggeeReference-derived type " + typeof(TProxy).Name + " does not have a (DkmProcess, ulong) constructor or cannot be instantiated.");
throw new NotSupportedException();
};
var type = typeof(TProxy);
// Make sure we have a constructor that takes a DkmProcess and a ulong. If we don't, we can't instantiate the type.
if (!type.IsAbstract) {
var ctor = type.GetConstructor(new[] { typeof(DkmProcess), typeof(ulong) });
if (ctor != null) {
var processParam = Expression.Parameter(typeof(DkmProcess));
var addressParam = Expression.Parameter(typeof(ulong));
var polymorphicParam = Expression.Parameter(typeof(bool));
nonPolymorphicFactory = Expression.Lambda<FactoryFunc>(
Expression.New(ctor, processParam, addressParam),
new[] { processParam, addressParam, polymorphicParam })
.Compile();
}
}
if (typeof(IPyObject).IsAssignableFrom(typeof(TProxy))) {

Просмотреть файл

@ -20,6 +20,7 @@ using System.Linq;
using Microsoft.Dia;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.Interop;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies {
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
@ -139,11 +140,6 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies {
return new { _process, _address }.GetHashCode();
}
protected TProxy GetFieldProxy<TProxy>(StructField<TProxy>? field, bool polymorphic = true)
where TProxy : IDataProxy {
return field.HasValue ? GetFieldProxy(field.Value) : default(TProxy);
}
protected TProxy GetFieldProxy<TProxy>(StructField<TProxy> field, bool polymorphic = true)
where TProxy : IDataProxy {
return DataProxy.Create<TProxy>(Process, Address.OffsetBy(field.Offset), polymorphic);

Просмотреть файл

@ -0,0 +1,45 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "_cframe", MinVersion = PythonLanguageVersion.V310, MaxVersion = PythonLanguageVersion.V310)]
[StructProxy(StructName = "_PyCFrame", MinVersion = PythonLanguageVersion.V311)]
internal class CFrameProxy : StructProxy {
internal class Fields {
[FieldProxy(MaxVersion = PythonLanguageVersion.V311)]
public StructField<Int32Proxy> use_tracing;
public StructField<PointerProxy<CFrameProxy>> previous;
[FieldProxy(MinVersion = PythonLanguageVersion.V311)]
public StructField<PointerProxy<PyInterpreterFrame>> current_frame;
}
private readonly Fields _fields;
public CFrameProxy(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public Int32Proxy use_tracing {
get { return GetFieldProxy(_fields.use_tracing); }
}
public PointerProxy<PyInterpreterFrame> current_frame => GetFieldProxy(_fields.current_frame);
}
}

Просмотреть файл

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "_import_state", MinVersion = PythonLanguageVersion.V312)]
internal class ImportState : StructProxy {
internal class Fields {
public StructField<PointerProxy<PyDictObject>> modules;
}
private readonly Fields _fields;
public ImportState(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public PointerProxy<PyDictObject> modules {
get { return GetFieldProxy(_fields.modules); }
}
}
}

Просмотреть файл

@ -14,60 +14,30 @@
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
internal class PyCodeObject : PyObject {
public class Fields {
public StructField<Int32Proxy> co_nlocals;
public StructField<PointerProxy<PyTupleObject>> co_names;
public StructField<PointerProxy<PyTupleObject>> co_varnames;
public StructField<PointerProxy<PyTupleObject>> co_freevars;
public StructField<PointerProxy<PyTupleObject>> co_cellvars;
public StructField<PointerProxy<IPyBaseStringObject>> co_filename;
public StructField<PointerProxy<IPyBaseStringObject>> co_name;
public StructField<Int32Proxy> co_firstlineno;
}
private readonly Fields _fields;
internal abstract class PyCodeObject : PyObject {
public PyCodeObject(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyCodeObject>();
}
public Int32Proxy co_nlocals {
get { return GetFieldProxy(_fields.co_nlocals); }
}
public abstract Int32Proxy co_nlocals { get; }
public PointerProxy<PyTupleObject> co_names {
get { return GetFieldProxy(_fields.co_names); }
}
public abstract PointerProxy<PyTupleObject> co_names { get; }
public PointerProxy<PyTupleObject> co_varnames {
get { return GetFieldProxy(_fields.co_varnames); }
}
public abstract IWritableDataProxy<PyTupleObject> co_varnames { get; }
public PointerProxy<PyTupleObject> co_freevars {
get { return GetFieldProxy(_fields.co_freevars); }
}
public abstract IWritableDataProxy<PyTupleObject> co_freevars { get; }
public PointerProxy<PyTupleObject> co_cellvars {
get { return GetFieldProxy(_fields.co_cellvars); }
}
public abstract IWritableDataProxy<PyTupleObject> co_cellvars { get; }
public PointerProxy<IPyBaseStringObject> co_filename {
get { return GetFieldProxy(_fields.co_filename); }
}
public abstract PointerProxy<IPyBaseStringObject> co_filename { get; }
public PointerProxy<IPyBaseStringObject> co_name {
get { return GetFieldProxy(_fields.co_name); }
}
public abstract PointerProxy<IPyBaseStringObject> co_name { get; }
public Int32Proxy co_firstlineno {
get { return GetFieldProxy(_fields.co_firstlineno); }
}
public abstract Int32Proxy co_firstlineno { get; }
public override void Repr(ReprBuilder builder) {
string name = co_name.TryRead().ToStringOrNull() ?? "???";

Просмотреть файл

@ -0,0 +1,60 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "PyCodeObject", MaxVersion = PythonLanguageVersion.V310)]
[PyType(MaxVersion = PythonLanguageVersion.V310, VariableName = "PyCode_Type")]
internal class PyCodeObject310 : PyCodeObject {
public class Fields {
public StructField<Int32Proxy> co_nlocals;
public StructField<PointerProxy<PyTupleObject>> co_names;
public StructField<PointerProxy<PyTupleObject>> co_varnames;
public StructField<PointerProxy<PyTupleObject>> co_freevars;
public StructField<PointerProxy<PyTupleObject>> co_cellvars;
public StructField<PointerProxy<IPyBaseStringObject>> co_filename;
public StructField<PointerProxy<IPyBaseStringObject>> co_name;
public StructField<Int32Proxy> co_firstlineno;
}
private readonly Fields _fields;
public override Int32Proxy co_nlocals => GetFieldProxy(_fields.co_nlocals);
public override PointerProxy<PyTupleObject> co_names => GetFieldProxy(_fields.co_names);
public override IWritableDataProxy<PyTupleObject> co_varnames => GetFieldProxy(_fields.co_varnames);
public override IWritableDataProxy<PyTupleObject> co_freevars => GetFieldProxy(_fields.co_freevars);
public override IWritableDataProxy<PyTupleObject> co_cellvars => GetFieldProxy(_fields.co_cellvars);
public override PointerProxy<IPyBaseStringObject> co_filename => GetFieldProxy(_fields.co_filename);
public override PointerProxy<IPyBaseStringObject> co_name => GetFieldProxy(_fields.co_name);
public override Int32Proxy co_firstlineno => GetFieldProxy(_fields.co_firstlineno);
public PyCodeObject310(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyCodeObject310>();
}
}
}

Просмотреть файл

@ -0,0 +1,134 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "PyCodeObject", MinVersion = PythonLanguageVersion.V311)]
[PyType(MinVersion = PythonLanguageVersion.V311, VariableName = "PyCode_Type")]
internal class PyCodeObject311 : PyCodeObject {
public class Fields {
public StructField<Int32Proxy> co_nlocals;
public StructField<PointerProxy<PyTupleObject>> co_names;
public StructField<PointerProxy<IPyBaseStringObject>> co_filename;
public StructField<PointerProxy<IPyBaseStringObject>> co_name;
public StructField<Int32Proxy> co_firstlineno;
public StructField<PointerProxy<PyTupleObject>> co_localsplusnames;
public StructField<PointerProxy<PyBytesObject>> co_localspluskinds;
public StructField<ArrayProxy<SByteProxy>> co_code_adaptive;
public StructField<Int32Proxy> _co_firsttraceable;
}
private readonly Fields _fields;
public override Int32Proxy co_nlocals => GetFieldProxy(_fields.co_nlocals);
public override PointerProxy<PyTupleObject> co_names => GetFieldProxy(_fields.co_names);
public override IWritableDataProxy<PyTupleObject> co_varnames => GetVariableTupleProxy(VariableKind.CO_FAST_LOCAL);
public override IWritableDataProxy<PyTupleObject> co_freevars => GetVariableTupleProxy(VariableKind.CO_FAST_FREE);
public override IWritableDataProxy<PyTupleObject> co_cellvars => GetVariableTupleProxy(VariableKind.CO_FAST_CELL);
public override PointerProxy<IPyBaseStringObject> co_filename => GetFieldProxy(_fields.co_filename);
public override PointerProxy<IPyBaseStringObject> co_name => GetFieldProxy(_fields.co_name);
public override Int32Proxy co_firstlineno => GetFieldProxy(_fields.co_firstlineno);
public ArrayProxy<SByteProxy> co_code_adaptive => GetFieldProxy(_fields.co_code_adaptive);
public Int32Proxy _co_firsttraceable => GetFieldProxy(_fields._co_firsttraceable);
public PyCodeObject311(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyCodeObject311>();
}
[Flags]
internal enum VariableKind {
CO_FAST_LOCAL = 0x20,
CO_FAST_CELL = 0x40,
CO_FAST_FREE = 0x80
}
private IWritableDataProxy<PyTupleObject> GetVariableTupleProxy(VariableKind kind) {
return new PyVariableTuplePointerProxy(Process, kind, GetFieldProxy(_fields.co_localsplusnames), GetFieldProxy(_fields.co_localspluskinds));
}
// In 3.11, the co_varnames, co_freevars, and co_cellvars were all removed. In order to not have to change our usage
// of them, this class and the PyVariableTuple were created in order to pretend to behave like the original co_varnames etc.
internal class PyVariableTuplePointerProxy : IWritableDataProxy<PyTupleObject> {
private readonly PyVariableTuple _pseudoTuple;
private readonly PointerProxy<PyTupleObject> _realTuple;
private readonly DkmProcess _process;
public PyVariableTuplePointerProxy(DkmProcess process, VariableKind kind, PointerProxy<PyTupleObject> localsplusnames, PointerProxy<PyBytesObject> localspluskinds) {
_pseudoTuple = new PyVariableTuple(process, kind, localsplusnames, localspluskinds);
_realTuple = localsplusnames;
_process = process;
}
public DkmProcess Process => _process;
public ulong Address => _realTuple.Address;
public long ObjectSize => _realTuple.ObjectSize;
public PyTupleObject Read() => _pseudoTuple;
public void Write(PyTupleObject value) => throw new NotImplementedException();
public void Write(object value) => throw new NotImplementedException();
object IValueStore.Read() => _pseudoTuple;
}
[PyType(Hidden = true)]
internal class PyVariableTuple : PyTupleObject {
private readonly VariableKind _kind;
private readonly PointerProxy<PyTupleObject> _localsplusnames;
private readonly PointerProxy<PyBytesObject> _localspluskinds;
private readonly DkmProcess _process;
public PyVariableTuple(DkmProcess process, VariableKind kind, PointerProxy<PyTupleObject> localsplusnames, PointerProxy<PyBytesObject> localspluskinds)
: base(process, localsplusnames.Read().Address) {
_kind = kind;
_localsplusnames = localsplusnames;
_localspluskinds = localspluskinds;
_process = process;
}
public override IEnumerable<PointerProxy<PyObject>> ReadElements() {
var localsplusnames = new List<PointerProxy<PyObject>>(_localsplusnames.Read().ReadElements());
var localspluskinds = _localspluskinds.Read().ToBytes();
Debug.Assert(localsplusnames.Count == localspluskinds.Length);
for (var i = 0; i < localsplusnames.Count; i++) {
var name = localsplusnames[i];
var kind = (VariableKind)localspluskinds[i];
if ((kind & _kind) == _kind) {
yield return name;
}
}
}
}
}
}

Просмотреть файл

@ -1,4 +1,4 @@
// Python Tools for Visual Studio
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
@ -21,68 +21,12 @@ using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.Evaluation;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "PyDictObject", MinVersion = PythonLanguageVersion.V39)]
[PyType(VariableName = "PyDict_Type", MinVersion = PythonLanguageVersion.V39)]
internal class PyDictObject : PyObject {
private class Fields {
public StructField<PointerProxy<PyDictKeysObject>> ma_keys;
public StructField<PointerProxy<ArrayProxy<PointerProxy<PyObject>>>> ma_values;
}
private readonly Fields _fields;
public PyDictObject(DkmProcess process, ulong address)
internal abstract class PyDictObject : PyObject {
protected PyDictObject(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyDictObject>();
}
public PointerProxy<PyDictKeysObject> ma_keys {
get { return GetFieldProxy(_fields.ma_keys); }
}
public PointerProxy<ArrayProxy<PointerProxy<PyObject>>> ma_values {
get { return GetFieldProxy(_fields.ma_values); }
}
public IEnumerable<KeyValuePair<PyObject, PointerProxy<PyObject>>> ReadElements() {
if (ma_keys.IsNull) {
yield break;
}
var keys = ma_keys.Read();
var size = keys.dk_size.Read();
if (size <= 0) {
yield break;
}
var n = keys.dk_nentries.Read();
var dk_entries = keys.dk_entries;
var entry = dk_entries[0];
if (!ma_values.IsNull) {
var values = ma_values.Read();
var value = values[0];
for (int i = 0; i < n; ++i) {
var key = entry.me_key;
if (!value.IsNull && !key.IsNull) {
yield return new KeyValuePair<PyObject, PointerProxy<PyObject>>(key.Read(), value);
}
entry = entry.GetAdjacentProxy(1);
value = value.GetAdjacentProxy(1);
}
} else {
for (int i = 0; i < n; ++i) {
var key = entry.me_key;
var value = entry.me_value;
if (!key.IsNull && !value.IsNull) {
yield return new KeyValuePair<PyObject, PointerProxy<PyObject>>(key.Read(), value);
}
entry = entry.GetAdjacentProxy(1);
}
}
}
public abstract IEnumerable<KeyValuePair<PyObject, PointerProxy<PyObject>>> ReadElements();
public override void Repr(ReprBuilder builder) {
var count = ReadElements().Count();
@ -114,75 +58,4 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
}
}
[StructProxy(MinVersion = PythonLanguageVersion.V39, StructName = "_dictkeysobject")]
internal class PyDictKeysObject : StructProxy {
public class Fields {
public StructField<SSizeTProxy> dk_size;
public StructField<SSizeTProxy> dk_nentries;
public StructField<ArrayProxy<SByteProxy>> dk_indices;
}
public readonly Fields _fields;
public SSizeTProxy dk_size {
get { return GetFieldProxy(_fields.dk_size); }
}
public SSizeTProxy dk_nentries {
get { return GetFieldProxy(_fields.dk_nentries); }
}
public ArrayProxy<PyDictKeyEntry> dk_entries {
get {
// dk_entries is located after dk_indices, which is
// variable length depending on the size of the table.
long size = dk_size.Read();
long offset = _fields.dk_indices.Offset;
if (size <= 0) {
return default(ArrayProxy<PyDictKeyEntry>);
} else if (size <= 0xFF) {
offset += size;
} else if (size <= 0xFFFF) {
offset += size * 2;
} else if (size <= 0xFFFFFFFF) {
offset += size * 4;
} else {
offset += size * 8;
}
return DataProxy.Create<ArrayProxy<PyDictKeyEntry>>(
Process,
Address.OffsetBy(offset)
);
}
}
public PyDictKeysObject(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
}
[StructProxy(MinVersion = PythonLanguageVersion.V39)]
internal class PyDictKeyEntry : StructProxy {
private class Fields {
public StructField<PointerProxy<PyObject>> me_key;
public StructField<PointerProxy<PyObject>> me_value;
}
private readonly Fields _fields;
public PyDictKeyEntry(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public PointerProxy<PyObject> me_key {
get { return GetFieldProxy(_fields.me_key); }
}
public PointerProxy<PyObject> me_value {
get { return GetFieldProxy(_fields.me_value); }
}
}
}
}

Просмотреть файл

@ -0,0 +1,157 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using System.Collections.Generic;
using System.Linq;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.Evaluation;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "PyDictObject", MinVersion = PythonLanguageVersion.V39, MaxVersion = PythonLanguageVersion.V310 )]
[PyType(VariableName = "PyDict_Type", MinVersion = PythonLanguageVersion.V39, MaxVersion = PythonLanguageVersion.V310)]
internal class PyDictObject310 : PyDictObject {
private class Fields {
public StructField<PointerProxy<PyDictKeysObject310>> ma_keys;
public StructField<PointerProxy<ArrayProxy<PointerProxy<PyObject>>>> ma_values;
}
private readonly Fields _fields;
public PyDictObject310(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyDictObject310>();
}
public PointerProxy<PyDictKeysObject310> ma_keys {
get { return GetFieldProxy(_fields.ma_keys); }
}
public PointerProxy<ArrayProxy<PointerProxy<PyObject>>> ma_values {
get { return GetFieldProxy(_fields.ma_values); }
}
public override IEnumerable<KeyValuePair<PyObject, PointerProxy<PyObject>>> ReadElements() {
if (ma_keys.IsNull) {
yield break;
}
var keys = ma_keys.Read();
var size = keys.dk_size.Read();
if (size <= 0) {
yield break;
}
var n = keys.dk_nentries.Read();
var dk_entries = keys.dk_entries;
var entry = dk_entries.First();
if (!ma_values.IsNull) {
var values = ma_values.Read();
var value = values[0];
for (int i = 0; i < n; ++i) {
var key = entry.me_key;
if (!value.IsNull && !key.IsNull) {
yield return new KeyValuePair<PyObject, PointerProxy<PyObject>>(key.Read(), value);
}
entry = entry.GetAdjacentProxy(1);
value = value.GetAdjacentProxy(1);
}
} else {
for (int i = 0; i < n; ++i) {
var key = entry.me_key;
var value = entry.me_value;
if (!key.IsNull && !value.IsNull) {
yield return new KeyValuePair<PyObject, PointerProxy<PyObject>>(key.Read(), value);
}
entry = entry.GetAdjacentProxy(1);
}
}
}
}
[StructProxy(MinVersion = PythonLanguageVersion.V39, MaxVersion = PythonLanguageVersion.V310, StructName = "_dictkeysobject")]
internal class PyDictKeysObject310 : StructProxy {
public class Fields {
public StructField<SSizeTProxy> dk_size;
public StructField<SSizeTProxy> dk_nentries;
public StructField<ArrayProxy<SByteProxy>> dk_indices;
}
public readonly Fields _fields;
public SSizeTProxy dk_size => GetFieldProxy(_fields.dk_size);
public SSizeTProxy dk_nentries {
get { return GetFieldProxy(_fields.dk_nentries); }
}
public IEnumerable<PyDictKeyEntry310> dk_entries {
get {
// dk_entries is located after dk_indices, which is
// variable length depending on the size of the table.
long size = dk_size.Read();
long offset = _fields.dk_indices.Offset;
if (size <= 0) {
return default(ArrayProxy<PyDictKeyEntry310>);
} else if (size <= 0xFF) {
offset += size;
} else if (size <= 0xFFFF) {
offset += size * 2;
} else if (size <= 0xFFFFFFFF) {
offset += size * 4;
} else {
offset += size * 8;
}
return DataProxy.Create<ArrayProxy<PyDictKeyEntry310>>(
Process,
Address.OffsetBy(offset)
);
}
}
public PyDictKeysObject310(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
}
[StructProxy(MinVersion = PythonLanguageVersion.V39, StructName = "PyDictKeyEntry")]
internal class PyDictKeyEntry310 : StructProxy {
private class Fields {
public StructField<PointerProxy<PyObject>> me_key;
public StructField<PointerProxy<PyObject>> me_value;
}
private readonly Fields _fields;
public PyDictKeyEntry310(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public PointerProxy<PyObject> me_key {
get { return GetFieldProxy(_fields.me_key); }
}
public PointerProxy<PyObject> me_value {
get { return GetFieldProxy(_fields.me_value); }
}
}
}

Просмотреть файл

@ -0,0 +1,208 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using System.Collections.Generic;
using System.Linq;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.Evaluation;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "PyDictObject", MinVersion = PythonLanguageVersion.V311)]
[PyType(VariableName = "PyDict_Type", MinVersion = PythonLanguageVersion.V311)]
internal class PyDictObject311 : PyDictObject {
private class Fields {
public StructField<PointerProxy<PyDictKeysObject311>> ma_keys;
public StructField<PointerProxy<ArrayProxy<PointerProxy<PyObject>>>> ma_values;
}
private readonly Fields _fields;
public PyDictObject311(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyDictObject311>();
}
public PointerProxy<PyDictKeysObject311> ma_keys {
get { return GetFieldProxy(_fields.ma_keys); }
}
public PointerProxy<ArrayProxy<PointerProxy<PyObject>>> ma_values {
get { return GetFieldProxy(_fields.ma_values); }
}
public override IEnumerable<KeyValuePair<PyObject, PointerProxy<PyObject>>> ReadElements() {
if (ma_keys.IsNull) {
yield break;
}
var keys = ma_keys.Read();
var size = 1 << (int)keys.dk_log2_size.Read();
if (size <= 0) {
yield break;
}
var n = keys.dk_nentries.Read();
if (!ma_values.IsNull) {
var values = ma_values.Read();
var value = values[0];
for (int i = 0; i < n; ++i) {
var entry = GetDkEntry(i);
var key = entry.me_key;
if (!value.IsNull && !key.IsNull) {
yield return new KeyValuePair<PyObject, PointerProxy<PyObject>>(key.Read(), value);
}
value = value.GetAdjacentProxy(1);
}
} else {
for (int i = 0; i < n; ++i) {
var entry = GetDkEntry(i);
var key = entry.me_key;
var value = entry.me_value;
if (!key.IsNull && !value.IsNull) {
yield return new KeyValuePair<PyObject, PointerProxy<PyObject>>(key.Read(), value);
}
}
}
}
private IDictKeyEntry GetDkEntry(int position) {
var keys = ma_keys.Read();
if (keys.dk_kind.Read() == 0) {
var entries = keys.dk_general_entries;
return entries[position];
} else {
var entries = keys.dk_unicode_entries;
return entries[position];
}
}
}
[StructProxy(MinVersion = PythonLanguageVersion.V311, StructName = "_dictkeysobject")]
internal class PyDictKeysObject311 : StructProxy {
public class Fields {
public StructField<SSizeTProxy> dk_log2_size;
public StructField<SSizeTProxy> dk_log2_index_bytes;
public StructField<SSizeTProxy> dk_nentries;
public StructField<ArrayProxy<SByteProxy>> dk_indices;
public StructField<ByteProxy> dk_kind;
}
public readonly Fields _fields;
public SSizeTProxy dk_log2_size => GetFieldProxy(_fields.dk_log2_size);
public SSizeTProxy dk_log2_index_bytes => GetFieldProxy(_fields.dk_log2_index_bytes);
public ByteProxy dk_kind => GetFieldProxy(_fields.dk_kind);
public SSizeTProxy dk_nentries {
get { return GetFieldProxy(_fields.dk_nentries); }
}
public ArrayProxy<PyDictKeyEntry311> dk_general_entries {
get {
// dk_entries is located after dk_indices, which is
// variable length depending on the size of the table.
long log_size = dk_log2_index_bytes.Read();
long offset = _fields.dk_indices.Offset;
offset += 1 << (int)log_size;
return DataProxy.Create<ArrayProxy<PyDictKeyEntry311>>(
Process,
Address.OffsetBy(offset)
);
}
}
public ArrayProxy<PyDictUnicodeEntry> dk_unicode_entries {
get {
// dk_entries is located after dk_indices, which is
// variable length depending on the size of the table.
long log_size = dk_log2_index_bytes.Read();
long offset = _fields.dk_indices.Offset;
offset += 1 << (int)log_size;
return DataProxy.Create<ArrayProxy<PyDictUnicodeEntry>>(
Process,
Address.OffsetBy(offset)
);
}
}
public PyDictKeysObject311(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
}
internal interface IDictKeyEntry : IDataProxy<IDictKeyEntry> {
public PointerProxy<PyObject> me_key { get; }
public PointerProxy<PyObject> me_value { get; }
}
[StructProxy(MinVersion = PythonLanguageVersion.V311, StructName ="PyDictKeyEntry")]
internal class PyDictKeyEntry311 : StructProxy, IDictKeyEntry {
private class Fields {
public StructField<PointerProxy<PyObject>> me_key;
public StructField<PointerProxy<PyObject>> me_value;
}
private readonly Fields _fields;
public PyDictKeyEntry311(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public PointerProxy<PyObject> me_key {
get { return GetFieldProxy(_fields.me_key); }
}
public PointerProxy<PyObject> me_value {
get { return GetFieldProxy(_fields.me_value); }
}
public IDictKeyEntry Read() => this;
}
[StructProxy(MinVersion = PythonLanguageVersion.V311)]
internal class PyDictUnicodeEntry : StructProxy, IDictKeyEntry {
private class Fields {
public StructField<PointerProxy<PyObject>> me_key;
public StructField<PointerProxy<PyObject>> me_value;
}
private readonly Fields _fields;
public PyDictUnicodeEntry(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public PointerProxy<PyObject> me_key {
get { return GetFieldProxy(_fields.me_key); }
}
public PointerProxy<PyObject> me_value {
get { return GetFieldProxy(_fields.me_value); }
}
public IDictKeyEntry Read() => this;
}
}

Просмотреть файл

@ -14,37 +14,16 @@
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.PythonTools.Common.Core.OS;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.CallStack;
using Microsoft.VisualStudio.Debugger.Evaluation;
using static Microsoft.VisualStudio.Threading.SingleThreadedSynchronizationContext;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(MinVersion = PythonLanguageVersion.V39, StructName = "_frame")]
internal class PyFrameObject : PyVarObject {
internal class Fields {
public StructField<PointerProxy<PyFrameObject>> f_back;
public StructField<PointerProxy<PyCodeObject>> f_code;
public StructField<PointerProxy<PyDictObject>> f_globals;
public StructField<PointerProxy<PyDictObject>> f_locals;
public StructField<Int32Proxy> f_lineno;
public StructField<ArrayProxy<PointerProxy<PyObject>>> f_localsplus;
}
private readonly Fields _fields;
internal abstract class PyFrameObject : PyVarObject {
public PyFrameObject(DkmProcess process, ulong address)
: base(process, address) {
var pythonInfo = process.GetPythonRuntimeInfo();
InitializeStruct(this, out _fields);
CheckPyType<PyFrameObject>();
}
@ -77,34 +56,26 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
var framePtrAddress = PyFrameObject.GetFramePtrAddress(frame, previousFrameCount);
if (framePtrAddress != 0) {
return new PyFrameObject(frame.Process, framePtrAddress);
var pythonInfo = process.GetPythonRuntimeInfo();
if (pythonInfo.LanguageVersion < PythonLanguageVersion.V311) {
return new PyFrameObject310(frame.Process, framePtrAddress);
}
return new PyFrameObject311(frame.Process, framePtrAddress);
}
return null;
}
public PointerProxy<PyFrameObject> f_back {
get { return GetFieldProxy(_fields.f_back); }
}
public abstract PointerProxy<PyFrameObject> f_back { get; }
public PointerProxy<PyCodeObject> f_code {
get { return GetFieldProxy(_fields.f_code); }
}
public abstract PointerProxy<PyCodeObject> f_code { get; }
public PointerProxy<PyDictObject> f_globals {
get { return GetFieldProxy(_fields.f_globals); }
}
public abstract PointerProxy<PyDictObject> f_globals { get; }
public PointerProxy<PyDictObject> f_locals {
get { return GetFieldProxy(_fields.f_locals); }
}
public abstract PointerProxy<PyDictObject> f_locals { get; }
public Int32Proxy f_lineno {
get { return GetFieldProxy(_fields.f_lineno); }
}
public abstract int ComputeLineNumber(DkmInspectionSession inspectionSession, DkmStackWalkFrame frame, DkmEvaluationFlags flags);
public ArrayProxy<PointerProxy<PyObject>> f_localsplus {
get { return GetFieldProxy(_fields.f_localsplus); }
}
public abstract ArrayProxy<PointerProxy<PyObject>> f_localsplus { get; }
private static ulong GetFramePtrAddress(DkmStackWalkFrame frame, int? previousFrameCount) {
// Frame address may already be stored in the frame, check the data.
@ -115,12 +86,12 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
var process = frame.Process;
var tid = frame.Thread.SystemPart.Id;
PyThreadState tstate = PyThreadState.GetThreadStates(process).FirstOrDefault(ts => ts.thread_id.Read() == tid);
PyFrameObject pyFrame = tstate.frame.Read();
PyFrameObject pyFrame = tstate.frame.TryRead();
if (pyFrame != null) {
// This pyFrame should be the topmost frame. We need to go down the callstack
// based on the number of previous frames that were already found.
var numberBack = previousFrameCount != null ? previousFrameCount.Value : 0;
while (numberBack > 0) {
while (numberBack > 0 && pyFrame.f_back.Process != null) {
pyFrame = pyFrame.f_back.Read();
numberBack--;
}

Просмотреть файл

@ -0,0 +1,57 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using Microsoft.VisualStudio.Debugger;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger.CallStack;
using Microsoft.VisualStudio.Debugger.Evaluation;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "_frame", MaxVersion = PythonLanguageVersion.V310)]
[PyType(MaxVersion = PythonLanguageVersion.V310, VariableName = "PyFrame_Type")]
internal class PyFrameObject310 : PyFrameObject {
internal class Fields {
public StructField<PointerProxy<PyFrameObject>> f_back;
public StructField<PointerProxy<PyCodeObject>> f_code;
public StructField<PointerProxy<PyDictObject>> f_globals;
public StructField<PointerProxy<PyDictObject>> f_locals;
public StructField<PointerProxy<PyObject>> f_trace;
public StructField<Int32Proxy> f_lineno;
public StructField<ArrayProxy<PointerProxy<PyObject>>> f_localsplus;
}
private readonly Fields _fields;
public override PointerProxy<PyFrameObject> f_back => GetFieldProxy(_fields.f_back);
public override PointerProxy<PyCodeObject> f_code => GetFieldProxy(_fields.f_code);
public override PointerProxy<PyDictObject> f_globals => GetFieldProxy(_fields.f_globals);
public override PointerProxy<PyDictObject> f_locals => GetFieldProxy(_fields.f_locals);
public override ArrayProxy<PointerProxy<PyObject>> f_localsplus => GetFieldProxy(_fields.f_localsplus);
public override int ComputeLineNumber(DkmInspectionSession inspectionSession, DkmStackWalkFrame frame, DkmEvaluationFlags flags) => GetFieldProxy(_fields.f_lineno).Read();
public PyFrameObject310(DkmProcess process, ulong address)
: base(process, address) {
var pythonInfo = process.GetPythonRuntimeInfo();
InitializeStruct(this, out _fields);
CheckPyType<PyFrameObject310>();
}
}
}

Просмотреть файл

@ -0,0 +1,92 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.CallStack;
using Microsoft.VisualStudio.Debugger.Evaluation;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "_frame", MinVersion = PythonLanguageVersion.V311)]
[PyType(MinVersion = PythonLanguageVersion.V311, VariableName = "PyFrame_Type")]
internal class PyFrameObject311 : PyFrameObject {
internal class Fields {
public StructField<PointerProxy<PyFrameObject>> f_back;
public StructField<PointerProxy<PyInterpreterFrame>> f_frame;
public StructField<PointerProxy<PyObject>> f_trace;
public StructField<Int32Proxy> f_lineno;
}
private readonly Fields _fields;
public PyFrameObject311(DkmProcess process, ulong address)
: base(process, address) {
var pythonInfo = process.GetPythonRuntimeInfo();
InitializeStruct(this, out _fields);
CheckPyType<PyFrameObject311>();
}
public PointerProxy<PyInterpreterFrame> f_frame {
get { return GetFieldProxy(_fields.f_frame); }
}
private PyInterpreterFrame GetFrame() {
return f_frame.TryRead();
}
public override PointerProxy<PyFrameObject> f_back {
get {
// See the code here https://github.com/python/cpython/blob/a0866f4c81ecc057d4521e8e7a02f4e1fff175a1/Objects/frameobject.c#L1491,
// f_back can be null when there is actually a frame because it's created lazily.
var back = GetFieldProxy(_fields.f_back);
if (back.IsNull) {
back = GetFrame().FindBackFrame();
}
return back;
}
}
public override PointerProxy<PyCodeObject> f_code => GetFrame().f_code;
public override PointerProxy<PyDictObject> f_globals => GetFrame().f_globals;
public override PointerProxy<PyDictObject> f_locals => GetFrame().f_locals;
public override ArrayProxy<PointerProxy<PyObject>> f_localsplus => GetFrame().f_localsplus;
public override int ComputeLineNumber(DkmInspectionSession inspectionSession, DkmStackWalkFrame frame, DkmEvaluationFlags flags) {
var setLineNumber = GetFieldProxy(_fields.f_lineno).Read();
if (setLineNumber == 0 && flags != DkmEvaluationFlags.None) {
// We need to use the CppExpressionEvaluator to compute the line number from
// our frame object. This function here: https://github.com/python/cpython/blob/46710ca5f263936a2e36fa5d0f140cf9f50b2618/Objects/frameobject.c#L40-L41
//
// However only do this when stopped at a breakpoint and evaluating the frame. Otherwise we it will fail and cause stepping
// to think the frame we eval is where we should stop.
var evaluator = new CppExpressionEvaluator(inspectionSession, 10, frame, DkmEvaluationFlags.TreatAsExpression);
var funcAddr = Process.GetPythonRuntimeInfo().DLLs.Python.GetFunctionAddress("PyFrame_GetLineNumber");
var frameAddr = Address;
try {
setLineNumber = evaluator.EvaluateInt32(string.Format("((int (*)(void *)){0})({1})", funcAddr, frameAddr), DkmEvaluationFlags.EnableExtendedSideEffects);
} catch (CppEvaluationException) {
// This means we can't evaluate right now, just leave as zero
setLineNumber = 0;
}
}
return setLineNumber;
}
}
}

Просмотреть файл

@ -0,0 +1,50 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "PyFunctionObject", MinVersion = PythonLanguageVersion.V310)]
internal class PyFunctionObject : PyObject {
internal class Fields {
public StructField<PointerProxy<PyObject>> func_globals;
public StructField<PointerProxy<PyObject>> func_builtins;
public StructField<PointerProxy<PyObject>> func_name;
public StructField<PointerProxy<PyObject>> func_qualname;
public StructField<PointerProxy<PyObject>> func_code;
public StructField<PointerProxy<PyObject>> func_defaults;
public StructField<PointerProxy<PyObject>> func_kwdefaults;
public StructField<PointerProxy<PyObject>> func_closure;
public StructField<PointerProxy<PyObject>> func_doc;
public StructField<PointerProxy<PyObject>> func_dict;
public StructField<PointerProxy<PyObject>> func_weakreflist;
public StructField<PointerProxy<PyObject>> func_module;
public StructField<PointerProxy<PyObject>> func_annotations;
public StructField<PointerProxy<UInt64Proxy>> vectorcall;
[FieldProxy(MinVersion = PythonLanguageVersion.V311)]
public StructField<UInt32Proxy> func_version;
}
private readonly Fields _fields;
public PyFunctionObject(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyFunctionObject>();
}
}
}

Просмотреть файл

@ -0,0 +1,109 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using System;
using System.Diagnostics;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
// This was added in commit https://github.com/python/cpython/commit/ae0a2b756255629140efcbe57fc2e714f0267aa3
[StructProxy(StructName = "_PyInterpreterFrame", MinVersion = PythonLanguageVersion.V311)]
internal class PyInterpreterFrame : StructProxy {
internal class Fields {
public StructField<PointerProxy<PyDictObject>> f_globals;
public StructField<PointerProxy<PyDictObject>> f_builtins;
public StructField<PointerProxy<PyDictObject>> f_locals;
[FieldProxy(MaxVersion = PythonLanguageVersion.V312)]
public StructField<PointerProxy<PyCodeObject>> f_code;
[FieldProxy(MinVersion = PythonLanguageVersion.V313)]
public StructField<PointerProxy<PyCodeObject>> f_executable;
public StructField<PointerProxy<PyFrameObject>> frame_obj;
public StructField<PointerProxy<PyInterpreterFrame>> previous;
public StructField<ArrayProxy<PointerProxy<PyObject>>> localsplus;
public StructField<CharProxy> owner;
}
private const int FRAME_OWNED_BY_THREAD = 0;
private const int FRAME_OWNED_BY_GENERATOR = 1;
private const int FRAME_OWNED_BY_FRAME_OBJECT = 2;
private const int FRAME_OWNED_BY_CSTACK = 3;
private readonly Fields _fields;
public PyInterpreterFrame(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public PointerProxy<PyCodeObject> f_code {
get {
// In 3.13, the f_code was renamed to f_executable
if (_fields.f_code.Process != null) {
return GetFieldProxy(_fields.f_code);
}
return GetFieldProxy(_fields.f_executable);
}
}
public PointerProxy<PyDictObject> f_globals {
get { return GetFieldProxy(_fields.f_globals); }
}
public PointerProxy<PyDictObject> f_locals {
get { return GetFieldProxy(_fields.f_locals); }
}
public ArrayProxy<PointerProxy<PyObject>> f_localsplus {
get { return GetFieldProxy(_fields.localsplus); }
}
public PointerProxy<PyFrameObject> frame_obj {
get { return GetFieldProxy(_fields.frame_obj); }
}
public PointerProxy<PyInterpreterFrame> previous => GetFieldProxy(_fields.previous);
private bool OwnedByThread() {
if (Process.GetPythonRuntimeInfo().LanguageVersion <= PythonLanguageVersion.V310) {
return true;
}
var owner = (GetFieldProxy(_fields.owner) as IValueStore).Read();
var charOwner = owner.ToString()[0];
return (int)charOwner == FRAME_OWNED_BY_THREAD;
}
public PointerProxy<PyFrameObject> FindBackFrame() {
// Trace.WriteLine("Searching for back frame ...");
var frame = previous.TryRead();
while (frame != null && !frame.OwnedByThread()) {
frame = frame.previous.TryRead();
}
// Make sure this frame_obj is pointing to this frame. Since the f_back
// of a PyFrameObject can be null even if it exists, the PyFrameObject we find
// through the list of PyInterpreterFrames may not have been created. We need
// to make sure it points to the PyInterpreterFrame we found so that subsequent calls
// to get things like its f_locals will work.
if (frame != null && !frame.frame_obj.IsNull) {
var obj = frame.frame_obj.Read() as PyFrameObject311;
obj.f_frame.Write(frame);
return frame.frame_obj;
}
return default(PointerProxy<PyFrameObject>);
}
}
}

Просмотреть файл

@ -15,6 +15,7 @@
// permissions and limitations under the License.
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
@ -23,8 +24,14 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
internal class PyInterpreterState : StructProxy {
private class Fields {
public StructField<PointerProxy<PyInterpreterState>> next;
[FieldProxy(MaxVersion = PythonLanguageVersion.V310)]
public StructField<PointerProxy<PyThreadState>> tstate_head;
[FieldProxy(MinVersion = PythonLanguageVersion.V311)]
public StructField<PyThreads> threads;
[FieldProxy(MaxVersion = PythonLanguageVersion.V311)]
public StructField<PointerProxy<PyDictObject>> modules;
[FieldProxy(MinVersion = PythonLanguageVersion.V312)]
public StructField<ImportState> imports;
public StructField<PointerProxy> eval_frame;
public StructField<ceval_state> ceval;
}
@ -49,11 +56,23 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
}
public PointerProxy<PyThreadState> tstate_head {
get { return GetFieldProxy(_fields.tstate_head); }
get {
if (_fields.tstate_head.Process != null) {
return GetFieldProxy(_fields.tstate_head);
}
var threads = GetFieldProxy(_fields.threads);
return threads.head;
}
}
public PointerProxy<PyDictObject> modules {
get { return GetFieldProxy(_fields.modules); }
get {
if (_fields.modules.Process != null) {
return GetFieldProxy(_fields.modules);
}
var imports = GetFieldProxy(_fields.imports);
return imports.modules;
}
}
public PointerProxy eval_frame {
@ -62,29 +81,31 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
public ceval_state ceval => GetFieldProxy(_fields.ceval);
private class InterpHeadHolder : DkmDataItem {
public readonly PointerProxy<PyInterpreterState> Proxy;
public InterpHeadHolder(DkmProcess process) {
var pyrtInfo = process.GetPythonRuntimeInfo();
Proxy = pyrtInfo.GetRuntimeState()?.interpreters.head
?? pyrtInfo.DLLs.Python.GetStaticVariable<PointerProxy<PyInterpreterState>>("interp_head");
}
}
public static PointerProxy<PyInterpreterState> interp_head(DkmProcess process) {
return process.GetOrCreateDataItem(() => new InterpHeadHolder(process)).Proxy;
}
public static IEnumerable<PyInterpreterState> GetInterpreterStates(DkmProcess process) {
for (var interp = interp_head(process).TryRead(); interp != null; interp = interp.next.TryRead()) {
yield return interp;
var pyrtInfo = process.GetPythonRuntimeInfo();
var runtimeState = pyrtInfo.GetRuntimeState();
var interpreters = runtimeState.interpreters;
var head = interpreters.head.TryRead();
while (head != null) {
yield return head;
head = head.next.TryRead();
}
}
public IEnumerable<PyThreadState> GetThreadStates() {
for (var tstate = tstate_head.TryRead(); tstate != null; tstate = tstate.next.TryRead()) {
yield return tstate;
public IEnumerable<PyThreadState> GetThreadStates(DkmProcess process) {
var pyrtInfo = process.GetPythonRuntimeInfo();
if (pyrtInfo.LanguageVersion <= PythonLanguageVersion.V310) {
for (var tstate = tstate_head.TryRead(); tstate != null; tstate = tstate.next.TryRead()) {
yield return tstate;
}
} else {
var threads = GetFieldProxy(_fields.threads);
var head = threads.head.TryRead();
while (head != null && head.Address != 0) {
yield return head;
head = head.next.TryRead();
}
}
}
@ -92,6 +113,7 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
public class ceval_state : StructProxy {
private class Fields {
public StructField<Int32Proxy> recursion_limit;
[FieldProxy(MaxVersion = PythonLanguageVersion.V39)]
public StructField<Int32Proxy> tracing_possible;
}

Просмотреть файл

@ -17,12 +17,16 @@
using System;
using System.Diagnostics;
using System.Numerics;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
internal class PyLongObject : PyVarObject {
private class Fields {
[FieldProxy(MaxVersion = Common.Parsing.PythonLanguageVersion.V311)]
public StructField<ByteProxy> ob_digit; // this is actually either uint16 or uint32, depending on Python bitness
[FieldProxy(MinVersion = PythonLanguageVersion.V312)]
public StructField<_PyLongValue> long_value;
}
private readonly Fields _fields;
@ -40,6 +44,16 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
}
public static PyLongObject Create(DkmProcess process, BigInteger value) {
// Use two different methods. PyLongObjects changed in 3.12. Instead of inheritance, we'll use an if statement so
// that we don't have to change classes derived from PyLongObject.
if (process.GetPythonRuntimeInfo().LanguageVersion < PythonLanguageVersion.V312) {
return Create11(process, value);
} else {
return Create12(process, value);
}
}
private static PyLongObject Create11(DkmProcess process, BigInteger value) {
var allocator = process.GetDataItem<PyObjectAllocator>();
Debug.Assert(allocator != null);
@ -77,12 +91,69 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
return result;
}
private static PyLongObject Create12(DkmProcess process, BigInteger value) {
var allocator = process.GetDataItem<PyObjectAllocator>();
Debug.Assert(allocator != null);
var bitsInDigit = process.Is64Bit() ? 30 : 15;
var bytesInDigit = process.Is64Bit() ? 4 : 2;
var absValue = BigInteger.Abs(value);
long numDigits = 0;
for (var t = absValue; t != 0;) {
++numDigits;
t >>= bitsInDigit;
}
var result = allocator.Allocate<PyLongObject>(numDigits * bytesInDigit);
// Size comes from here:
// https://github.com/python/cpython/blob/a0866f4c81ecc057d4521e8e7a02f4e1fff175a1/Objects/longobject.c#L158
var fields = StructProxy.GetStructFields<_PyLongValue, _PyLongValue.Fields>(process);
long ob_size = numDigits * bytesInDigit + fields.ob_digit.Offset;
result.ob_size.Write(ob_size);
// Digits are stored in the long_value.lv_data field in 3.12
result.long_value.WriteTag((ulong)numDigits, value == 0, value < 0);
// Then we write the data out one digit at a time
if (bitsInDigit == 15) {
for (var digitPtr = new UInt16Proxy(process, result.ob_digit.Address); absValue != 0; digitPtr = digitPtr.GetAdjacentProxy(1)) {
digitPtr.Write((ushort)(absValue % (1 << bitsInDigit)));
absValue >>= bitsInDigit;
}
} else {
for (var digitPtr = new UInt32Proxy(process, result.ob_digit.Address); absValue != 0; digitPtr = digitPtr.GetAdjacentProxy(1)) {
digitPtr.Write((uint)(absValue % (1 << bitsInDigit)));
absValue >>= bitsInDigit;
}
}
return result;
}
private ByteProxy ob_digit {
get { return GetFieldProxy(_fields.ob_digit); }
get {
if (_fields.ob_digit.Process != null) {
return GetFieldProxy(_fields.ob_digit);
} else {
return GetFieldProxy(_fields.long_value).ob_digit;
}
}
}
public BigInteger ToBigInteger() {
if (Process.GetPythonRuntimeInfo().LanguageVersion < PythonLanguageVersion.V312) {
return ToBigInteger11();
} else {
return ToBigInteger12();
}
}
public _PyLongValue long_value => GetFieldProxy(_fields.long_value);
private BigInteger ToBigInteger11() {
var bitsInDigit = Process.Is64Bit() ? 30 : 15;
long ob_size = this.ob_size.Read();
@ -112,8 +183,86 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
return ob_size > 0 ? result : -result;
}
public BigInteger ToBigInteger12() {
var bitsInDigit = Process.Is64Bit() ? 30 : 15;
var long_value = GetFieldProxy(_fields.long_value);
long count = long_value.digit_count;
// Read and parse digits in reverse, starting from the most significant ones.
var result = new BigInteger(0);
if (bitsInDigit == 15) {
var digitPtr = new UInt16Proxy(Process, ob_digit.Address).GetAdjacentProxy(count);
for (long i = 0; i != count; ++i) {
digitPtr = digitPtr.GetAdjacentProxy(-1);
result <<= bitsInDigit;
result += digitPtr.Read();
}
} else {
var digitPtr = new UInt32Proxy(Process, ob_digit.Address).GetAdjacentProxy(count);
for (long i = 0; i != count; ++i) {
digitPtr = digitPtr.GetAdjacentProxy(-1);
result <<= bitsInDigit;
result += digitPtr.Read();
}
}
return long_value.is_negative ? -result : result;
}
public override void Repr(ReprBuilder builder) {
builder.AppendLiteral(ToBigInteger());
}
[StructProxy(MinVersion = PythonLanguageVersion.V312, StructName = "_PyLongValue")]
public class _PyLongValue : StructProxy {
public class Fields {
public StructField<UInt64Proxy> lv_tag;
public StructField<ByteProxy> ob_digit;
}
private const int SIGN_ZERO = 1;
private const int SIGN_NEGATIVE = 2;
private const int NON_SIZE_BITS = 3;
private const int SIGN_MASK = 3;
private readonly Fields _fields;
public _PyLongValue(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public ByteProxy ob_digit => GetFieldProxy(_fields.ob_digit);
public UInt64Proxy lv_tag => GetFieldProxy(_fields.lv_tag);
public void WriteTag(ulong numberDigits, bool isZero, bool isNegative) {
ulong lv_tag = numberDigits << NON_SIZE_BITS;
if (isZero) {
lv_tag |= SIGN_ZERO;
} else if (isNegative) {
lv_tag |= SIGN_NEGATIVE;
}
this.lv_tag.Write(lv_tag);
}
public uint digit_count {
get {
return (uint)(lv_tag.Read() >> NON_SIZE_BITS);
}
set {
lv_tag.Write((value << NON_SIZE_BITS) | (lv_tag.Read() & SIGN_MASK));
}
}
public bool is_negative {
get {
return (lv_tag.Read() & SIGN_MASK) == SIGN_NEGATIVE;
}
}
}
}
}

Просмотреть файл

@ -30,6 +30,7 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
public string VariableName { get; set; }
public PythonLanguageVersion MinVersion { get; set; }
public PythonLanguageVersion MaxVersion { get; set; }
public bool Hidden { get; set; }
}
internal interface IPyObject : IValueStore<PyObject>, IDataProxy<StructProxy> {
@ -81,6 +82,9 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
if (pyTypeAttr.MaxVersion != PythonLanguageVersion.None && langVer > pyTypeAttr.MaxVersion) {
continue;
}
if (pyTypeAttr.Hidden) {
continue;
}
typeVarName = pyTypeAttr.VariableName ?? ComputeVariableName(proxyType);
break;
@ -169,7 +173,12 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
} else if (dictoffset < 0) {
var varObj = this as PyVarObject;
if (varObj == null) {
throw new InvalidDataException();
// Prior to 3.11 this should have been an error. In 3.11 many objects have negative tp_dictoffset
if (Process.GetPythonRuntimeInfo().LanguageVersion <= PythonLanguageVersion.V310) {
Debug.Fail("Non-var object with negative tp_dictoffset.");
throw new InvalidOperationException();
}
return null;
}
long size = ob_type.tp_basicsize.Read();

Просмотреть файл

@ -32,6 +32,7 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
public StructField<BoolProxy> core_initialized;
public StructField<BoolProxy> initialized;
public StructField<pyinterpreters> interpreters;
public StructField<gilstate_runtime_state> gilstate;
}
private readonly Fields _fields;
@ -44,6 +45,7 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
public BoolProxy core_initialized => GetFieldProxy(_fields.core_initialized);
public BoolProxy initialized => GetFieldProxy(_fields.initialized);
public pyinterpreters interpreters => GetFieldProxy(_fields.interpreters);
public gilstate_runtime_state gilstate => GetFieldProxy(_fields.gilstate);
[StructProxy(MinVersion = PythonLanguageVersion.V39, StructName = "pyinterpreters")]
@ -64,6 +66,22 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
public PointerProxy<PyInterpreterState> main => GetFieldProxy(_fields.main);
}
[StructProxy(MinVersion = PythonLanguageVersion.V312, StructName = "_gilstate_runtime_state")]
public class gilstate_runtime_state : StructProxy {
public class Fields {
public StructField<Int32Proxy> check_enabled;
}
private readonly Fields _fields;
public gilstate_runtime_state(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public Int32Proxy check_enabled => GetFieldProxy(_fields.check_enabled);
}
}

Просмотреть файл

@ -24,15 +24,27 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
internal class PyThreadState : StructProxy {
private class Fields {
public StructField<PointerProxy<PyThreadState>> next;
[FieldProxy(MaxVersion = PythonLanguageVersion.V310)]
public StructField<PointerProxy<PyFrameObject>> frame;
[FieldProxy(MinVersion = PythonLanguageVersion.V311)]
public StructField<PointerProxy<PyInterpreterState>> interp;
[FieldProxy(MaxVersion = PythonLanguageVersion.V39)]
public StructField<Int32Proxy> use_tracing;
[FieldProxy(MinVersion = PythonLanguageVersion.V310, MaxVersion = PythonLanguageVersion.V312)]
public StructField<PointerProxy<CFrameProxy>> cframe;
public StructField<PointerProxy> c_tracefunc;
[FieldProxy(MaxVersion = PythonLanguageVersion.V311)]
public StructField<PointerProxy<PyObject>> curexc_type;
[FieldProxy(MaxVersion = PythonLanguageVersion.V311)]
public StructField<PointerProxy<PyObject>> curexc_value;
public StructField<PointerProxy<PyObject>> curexc_traceback;
public StructField<PyErr_StackItem> exc_state;
[FieldProxy(MinVersion = PythonLanguageVersion.V312)]
public StructField<PointerProxy<PyBaseExceptionObject>> current_exception;
public StructField<PointerProxy<PyErr_StackItem>> exc_info;
public StructField<Int32Proxy> thread_id;
[FieldProxy(MinVersion = PythonLanguageVersion.V312)]
public StructField<Int32Proxy> tracing; // Indicates if sys.monitoring is set, not something we set here.
[FieldProxy(MinVersion = PythonLanguageVersion.V313)]
public StructField<PointerProxy<PyInterpreterFrame>> current_frame;
}
private readonly Fields _fields;
@ -54,11 +66,27 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
}
public PointerProxy<PyFrameObject> frame {
get { return GetFieldProxy(_fields.frame); }
get {
if (_fields.frame.Process != null) {
return GetFieldProxy(_fields.frame);
}
// In 3.11, the current frame was moved into the cframe
if (_fields.cframe.Process != null) {
var cframe = GetFieldProxy(_fields.cframe).Read();
var interpFrame311 = cframe.current_frame.TryRead();
return interpFrame311.frame_obj;
}
// In 3.13, cframe was removed and the current_frame was just placed
// in the thread.
var interpFrame = GetFieldProxy(_fields.current_frame).TryRead();
return interpFrame.frame_obj;
}
}
public Int32Proxy use_tracing {
get { return GetFieldProxy(_fields.use_tracing); }
public PointerProxy<CFrameProxy> cframe {
get { return GetFieldProxy(_fields.cframe); }
}
public PointerProxy c_tracefunc {
@ -66,33 +94,52 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
}
public PointerProxy<PyObject> curexc_type {
get { return GetFieldProxy(_fields.curexc_type); }
get {
if (_fields.curexc_type.Process != null) {
return GetFieldProxy(_fields.curexc_type);
}
// In 3.12, the current exception was stored by itself instead of separately with the
// type and value.
var exc = GetFieldProxy(_fields.current_exception).Read();
return exc.ob_type.ReinterpretCast<PyObject>();
}
}
public PointerProxy<PyObject> curexc_value {
get { return GetFieldProxy(_fields.curexc_value); }
get {
if (_fields.curexc_value.Process != null) {
return GetFieldProxy(_fields.curexc_value);
}
// In 3.12, the current exception was stored by itself instead of separately with the
// type and value.
return GetFieldProxy(_fields.current_exception).ReinterpretCast<PyObject>();
}
}
public PointerProxy<PyObject> curexc_traceback {
get { return GetFieldProxy(_fields.curexc_traceback); }
}
public PointerProxy<PyObject> exc_type(PythonLanguageVersion version) =>
GetFieldProxy(_fields.exc_state).exc_type;
public PointerProxy<PyObject> exc_value(PythonLanguageVersion version) =>
GetFieldProxy(_fields.exc_state).exc_value;
public PointerProxy<PyObject> exc_traceback(PythonLanguageVersion version) =>
GetFieldProxy(_fields.exc_state).exc_traceback;
public Int32Proxy thread_id {
get { return GetFieldProxy(_fields.thread_id); }
}
public static IEnumerable<PyThreadState> GetThreadStates(DkmProcess process) {
return PyInterpreterState.GetInterpreterStates(process).SelectMany(interp => interp.GetThreadStates());
return PyInterpreterState.GetInterpreterStates(process).SelectMany(interp => interp.GetThreadStates(process));
}
public void RegisterTracing(ulong traceFunc) {
// In 3.10, the use_tracing flag sets tracing for the thread.
if (_fields.use_tracing.Process != null) {
GetFieldProxy(_fields.use_tracing).Write(1);
}
// In 3.11 the cframe has the use_tracing flag, but must be set to 255.
if (_fields.cframe.Process != null && Process.GetPythonRuntimeInfo().LanguageVersion == PythonLanguageVersion.V311) {
var frame = cframe.Read();
frame.use_tracing.Write(255);
}
// In 3.12, there's no longer a use_tracing flag. We have to register tracing through an API instead.
if (Process.GetPythonRuntimeInfo().LanguageVersion <= PythonLanguageVersion.V311) {
c_tracefunc.Write(traceFunc);
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// Python Tools for Visual Studio
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(StructName = "pythreads", MinVersion = PythonLanguageVersion.V311)]
internal class PyThreads: StructProxy {
internal class Fields {
public StructField<UInt64Proxy> next_unique_id;
public StructField<PointerProxy<PyThreadState>> head;
public StructField<Int64Proxy> count;
public StructField<UInt64Proxy> stacksize;
}
private readonly Fields _fields;
public PyThreads(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public PointerProxy<PyThreadState> head {
get { return GetFieldProxy(_fields.head); }
}
}
}

Просмотреть файл

@ -38,7 +38,7 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
get { return GetFieldProxy(_fields.ob_item); }
}
public IEnumerable<PointerProxy<PyObject>> ReadElements() {
public virtual IEnumerable<PointerProxy<PyObject>> ReadElements() {
return ob_item.Take(ob_size.Read());
}

Просмотреть файл

@ -23,176 +23,11 @@ using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.Evaluation;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(MinVersion = PythonLanguageVersion.V39, StructName = "PyUnicodeObject")]
[PyType(MinVersion = PythonLanguageVersion.V39, VariableName = "PyUnicode_Type")]
internal class PyUnicodeObject : PyVarObject, IPyBaseStringObject {
private static readonly Encoding _latin1 = Encoding.GetEncoding("Latin1");
private enum PyUnicode_Kind {
PyUnicode_WCHAR_KIND = 0,
PyUnicode_1BYTE_KIND = 1,
PyUnicode_2BYTE_KIND = 2,
PyUnicode_4BYTE_KIND = 4
}
private enum Interned {
SSTATE_NOT_INTERNED = 0,
SSTATE_INTERNED_MORTAL = 1,
SSTATE_INTERNED_IMMORTAL = 2
}
private struct State {
private static readonly BitVector32.Section
internedSection = BitVector32.CreateSection(2),
kindSection = BitVector32.CreateSection(4, internedSection),
compactSection = BitVector32.CreateSection(1, kindSection),
asciiSection = BitVector32.CreateSection(1, compactSection),
readySection = BitVector32.CreateSection(1, asciiSection);
private BitVector32 _state;
private State(byte state) {
_state = new BitVector32(state);
}
public static explicit operator State(byte state) {
return new State(state);
}
public static explicit operator byte(State state) {
return (byte)state._state.Data;
}
public Interned interned {
get { return (Interned)_state[internedSection]; }
set { _state[internedSection] = (int)value; }
}
public PyUnicode_Kind kind {
get { return (PyUnicode_Kind)_state[kindSection]; }
set { _state[kindSection] = (int)value; }
}
public bool compact {
get { return _state[compactSection] != 0; }
set { _state[compactSection] = value ? 1 : 0; }
}
public bool ascii {
get { return _state[asciiSection] != 0; }
set { _state[asciiSection] = value ? 1 : 0; }
}
public bool ready {
get { return _state[readySection] != 0; }
set { _state[readySection] = value ? 1 : 0; }
}
}
public class Fields {
public StructField<PointerProxy> data;
}
private readonly Fields _fields;
private readonly PyASCIIObject _asciiObject;
private readonly PyCompactUnicodeObject _compactObject;
internal abstract class PyUnicodeObject : PyVarObject, IPyBaseStringObject {
protected static readonly Encoding _latin1 = Encoding.GetEncoding("Latin1");
public PyUnicodeObject(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyUnicodeObject>();
_asciiObject = new PyASCIIObject(process, address);
_compactObject = new PyCompactUnicodeObject(process, address);
}
public static PyUnicodeObject Create(DkmProcess process, string value) {
var allocator = process.GetDataItem<PyObjectAllocator>();
Debug.Assert(allocator != null);
var result = allocator.Allocate<PyUnicodeObject>(value.Length * sizeof(char));
result._asciiObject.hash.Write(-1);
result._asciiObject.length.Write(value.Length);
result._compactObject.wstr_length.Write(value.Length);
var state = new State {
interned = Interned.SSTATE_NOT_INTERNED,
kind = PyUnicode_Kind.PyUnicode_2BYTE_KIND,
compact = true,
ascii = false,
ready = true
};
result._asciiObject.state.Write((byte)state);
ulong dataPtr = result.Address.OffsetBy(StructProxy.SizeOf<PyCompactUnicodeObject>(process));
result._asciiObject.wstr.Write(dataPtr);
process.WriteMemory(dataPtr, Encoding.Unicode.GetBytes(value));
return result;
}
public override string ToString() {
byte[] buf;
State state = (State)_asciiObject.state.Read();
if (state.ascii) {
state.kind = PyUnicode_Kind.PyUnicode_1BYTE_KIND;
}
if (!state.ready) {
ulong wstr = _asciiObject.wstr.Read();
if (wstr == 0) {
return null;
}
uint wstr_length = checked((uint)_compactObject.wstr_length.Read());
if (wstr_length == 0) {
return "";
}
buf = new byte[wstr_length * 2];
Process.ReadMemory(wstr, DkmReadMemoryFlags.None, buf);
return Encoding.Unicode.GetString(buf, 0, buf.Length);
}
int length = checked((int)_asciiObject.length.Read());
if (length == 0) {
return "";
}
ulong data;
if (!state.compact) {
data = GetFieldProxy(_fields.data).Read();
} else if (state.ascii) {
data = Address.OffsetBy(StructProxy.SizeOf<PyASCIIObject>(Process));
} else {
data = Address.OffsetBy(StructProxy.SizeOf<PyCompactUnicodeObject>(Process));
}
if (data == 0) {
return null;
}
buf = new byte[length * (int)state.kind];
Process.ReadMemory(data, DkmReadMemoryFlags.None, buf);
Encoding enc;
switch (state.kind) {
case PyUnicode_Kind.PyUnicode_1BYTE_KIND:
enc = _latin1;
break;
case PyUnicode_Kind.PyUnicode_2BYTE_KIND:
enc = Encoding.Unicode;
break;
case PyUnicode_Kind.PyUnicode_4BYTE_KIND:
enc = Encoding.UTF32;
break;
default:
Debug.Fail("Unsupported PyUnicode_Kind " + state.kind);
return null;
}
return enc.GetString(buf, 0, buf.Length);
}
public override void Repr(ReprBuilder builder) {
@ -214,54 +49,15 @@ namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
public static explicit operator string(PyUnicodeObject obj) {
return (object)obj == null ? null : obj.ToString();
}
}
internal class PyASCIIObject : StructProxy {
public class Fields {
public StructField<SSizeTProxy> length;
public StructField<SSizeTProxy> hash;
public StructField<ByteProxy> state;
public StructField<PointerProxy> wstr;
}
private readonly Fields _fields;
public PyASCIIObject(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public SSizeTProxy length {
get { return GetFieldProxy(_fields.length); }
}
public SSizeTProxy hash {
get { return GetFieldProxy(_fields.hash); }
}
public ByteProxy state {
get { return GetFieldProxy(_fields.state); }
}
public PointerProxy wstr {
get { return GetFieldProxy(_fields.wstr); }
public static PyUnicodeObject Create(DkmProcess process, string value) {
if (process.GetPythonRuntimeInfo().LanguageVersion <= PythonLanguageVersion.V311) {
return PyUnicodeObject311.Create311(process, value);
} else {
return PyUnicodeObject312.Create312(process, value);
}
}
}
internal class PyCompactUnicodeObject : StructProxy {
public class Fields {
public StructField<SSizeTProxy> wstr_length;
}
private readonly Fields _fields;
public PyCompactUnicodeObject(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public SSizeTProxy wstr_length {
get { return GetFieldProxy(_fields.wstr_length); }
}
}
}

Просмотреть файл

@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(MinVersion = PythonLanguageVersion.V39, MaxVersion = PythonLanguageVersion.V311, StructName = "PyUnicodeObject")]
[PyType(MinVersion = PythonLanguageVersion.V39, MaxVersion = PythonLanguageVersion.V311, VariableName = "PyUnicode_Type")]
internal class PyUnicodeObject311 : PyUnicodeObject {
private enum PyUnicode_Kind {
PyUnicode_WCHAR_KIND = 0,
PyUnicode_1BYTE_KIND = 1,
PyUnicode_2BYTE_KIND = 2,
PyUnicode_4BYTE_KIND = 4
}
private enum Interned {
SSTATE_NOT_INTERNED = 0,
SSTATE_INTERNED_MORTAL = 1,
SSTATE_INTERNED_IMMORTAL = 2
}
private struct State {
private static readonly BitVector32.Section
internedSection = BitVector32.CreateSection(2),
kindSection = BitVector32.CreateSection(4, internedSection),
compactSection = BitVector32.CreateSection(1, kindSection),
asciiSection = BitVector32.CreateSection(1, compactSection),
readySection = BitVector32.CreateSection(1, asciiSection);
private BitVector32 _state;
private State(byte state) {
_state = new BitVector32(state);
}
public static explicit operator State(byte state) {
return new State(state);
}
public static explicit operator byte(State state) {
return (byte)state._state.Data;
}
public Interned interned {
get { return (Interned)_state[internedSection]; }
set { _state[internedSection] = (int)value; }
}
public PyUnicode_Kind kind {
get { return (PyUnicode_Kind)_state[kindSection]; }
set { _state[kindSection] = (int)value; }
}
public bool compact {
get { return _state[compactSection] != 0; }
set { _state[compactSection] = value ? 1 : 0; }
}
public bool ascii {
get { return _state[asciiSection] != 0; }
set { _state[asciiSection] = value ? 1 : 0; }
}
public bool ready {
get { return _state[readySection] != 0; }
set { _state[readySection] = value ? 1 : 0; }
}
}
public class Fields {
public StructField<PointerProxy> data;
}
private readonly Fields _fields;
private readonly PyASCIIObject311 _asciiObject;
private readonly PyCompactUnicodeObject311 _compactObject;
public PyUnicodeObject311(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyUnicodeObject311>();
_asciiObject = new PyASCIIObject311(process, address);
_compactObject = new PyCompactUnicodeObject311(process, address);
}
public static PyUnicodeObject311 Create311(DkmProcess process, string value) {
var allocator = process.GetDataItem<PyObjectAllocator>();
Debug.Assert(allocator != null);
var result = allocator.Allocate<PyUnicodeObject311>(value.Length * sizeof(char));
result._asciiObject.hash.Write(-1);
result._asciiObject.length.Write(value.Length);
result._compactObject.wstr_length.Write(value.Length);
var state = new State {
interned = Interned.SSTATE_NOT_INTERNED,
kind = PyUnicode_Kind.PyUnicode_2BYTE_KIND,
compact = true,
ascii = false,
ready = true
};
result._asciiObject.state.Write((byte)state);
ulong dataPtr = result.Address.OffsetBy(StructProxy.SizeOf<PyCompactUnicodeObject311>(process));
result._asciiObject.wstr.Write(dataPtr);
process.WriteMemory(dataPtr, Encoding.Unicode.GetBytes(value));
return result;
}
public override string ToString() {
byte[] buf;
State state = (State)_asciiObject.state.Read();
if (state.ascii) {
state.kind = PyUnicode_Kind.PyUnicode_1BYTE_KIND;
}
if (!state.ready) {
ulong wstr = _asciiObject.wstr.Read();
if (wstr == 0) {
return null;
}
uint wstr_length = checked((uint)_compactObject.wstr_length.Read());
if (wstr_length == 0) {
return "";
}
buf = new byte[wstr_length * 2];
Process.ReadMemory(wstr, DkmReadMemoryFlags.None, buf);
return Encoding.Unicode.GetString(buf, 0, buf.Length);
}
int length = checked((int)_asciiObject.length.Read());
if (length == 0) {
return "";
}
ulong data;
if (!state.compact) {
data = GetFieldProxy(_fields.data).Read();
} else if (state.ascii) {
data = Address.OffsetBy(StructProxy.SizeOf<PyASCIIObject311>(Process));
} else {
data = Address.OffsetBy(StructProxy.SizeOf<PyCompactUnicodeObject311>(Process));
}
if (data == 0) {
return null;
}
buf = new byte[length * (int)state.kind];
Process.ReadMemory(data, DkmReadMemoryFlags.None, buf);
Encoding enc;
switch (state.kind) {
case PyUnicode_Kind.PyUnicode_1BYTE_KIND:
enc = _latin1;
break;
case PyUnicode_Kind.PyUnicode_2BYTE_KIND:
enc = Encoding.Unicode;
break;
case PyUnicode_Kind.PyUnicode_4BYTE_KIND:
enc = Encoding.UTF32;
break;
default:
Debug.Fail("Unsupported PyUnicode_Kind " + state.kind);
return null;
}
return enc.GetString(buf, 0, buf.Length);
}
}
[StructProxy(MaxVersion = PythonLanguageVersion.V311, StructName = "PyASCIIObject")]
[PyType(MinVersion = PythonLanguageVersion.V39, MaxVersion = PythonLanguageVersion.V311, VariableName = "PyASCII_Type")]
internal class PyASCIIObject311 : StructProxy {
public class Fields {
public StructField<SSizeTProxy> length;
public StructField<SSizeTProxy> hash;
public StructField<ByteProxy> state;
public StructField<PointerProxy> wstr;
}
private readonly Fields _fields;
public PyASCIIObject311(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public SSizeTProxy length {
get { return GetFieldProxy(_fields.length); }
}
public SSizeTProxy hash {
get { return GetFieldProxy(_fields.hash); }
}
public ByteProxy state {
get { return GetFieldProxy(_fields.state); }
}
public PointerProxy wstr => GetFieldProxy(_fields.wstr);
}
[StructProxy(MaxVersion = PythonLanguageVersion.V311, StructName = "PyCompactUnicodeObject")]
[PyType(MinVersion = PythonLanguageVersion.V39, MaxVersion = PythonLanguageVersion.V311, VariableName = "PyCompactUnicode_Type")]
internal class PyCompactUnicodeObject311 : StructProxy {
public class Fields {
public StructField<SSizeTProxy> wstr_length;
}
private readonly Fields _fields;
public PyCompactUnicodeObject311(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public SSizeTProxy wstr_length {
get { return GetFieldProxy(_fields.wstr_length); }
}
}
}

Просмотреть файл

@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.PythonTools.Common.Core.OS;
using Microsoft.PythonTools.Common.Parsing;
using Microsoft.VisualStudio.Debugger;
namespace Microsoft.PythonTools.Debugger.Concord.Proxies.Structs {
[StructProxy(MinVersion = PythonLanguageVersion.V312, StructName = "PyUnicodeObject")]
[PyType(MinVersion = PythonLanguageVersion.V312, VariableName = "PyUnicode_Type")]
internal class PyUnicodeObject312 : PyUnicodeObject {
private enum PyUnicode_Kind {
PyUnicode_1BYTE_KIND = 1,
PyUnicode_2BYTE_KIND = 2,
PyUnicode_4BYTE_KIND = 4
}
private enum Interned {
SSTATE_NOT_INTERNED = 0,
SSTATE_INTERNED_MORTAL = 1,
SSTATE_INTERNED_IMMORTAL = 2
}
private struct State {
private static readonly BitVector32.Section
internedSection = BitVector32.CreateSection(2),
kindSection = BitVector32.CreateSection(4, internedSection),
compactSection = BitVector32.CreateSection(1, kindSection),
asciiSection = BitVector32.CreateSection(1, compactSection),
readySection = BitVector32.CreateSection(1, asciiSection);
private BitVector32 _state;
private State(byte state) {
_state = new BitVector32(state);
}
public static explicit operator State(byte state) {
return new State(state);
}
public static explicit operator byte(State state) {
return (byte)state._state.Data;
}
public Interned interned {
get { return (Interned)_state[internedSection]; }
set { _state[internedSection] = (int)value; }
}
public PyUnicode_Kind kind {
get { return (PyUnicode_Kind)_state[kindSection]; }
set { _state[kindSection] = (int)value; }
}
public bool compact {
get { return _state[compactSection] != 0; }
set { _state[compactSection] = value ? 1 : 0; }
}
public bool ascii {
get { return _state[asciiSection] != 0; }
set { _state[asciiSection] = value ? 1 : 0; }
}
public bool ready {
get { return _state[readySection] != 0; }
set { _state[readySection] = value ? 1 : 0; }
}
}
public class Fields {
public StructField<PointerProxy> data;
}
private readonly Fields _fields;
private readonly PyASCIIObject312 _asciiObject;
private readonly PyCompactUnicodeObject312 _compactObject;
public PyUnicodeObject312(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
CheckPyType<PyUnicodeObject312>();
_asciiObject = new PyASCIIObject312(process, address);
_compactObject = new PyCompactUnicodeObject312(process, address);
}
public static PyUnicodeObject312 Create312(DkmProcess process, string value) {
var allocator = process.GetDataItem<PyObjectAllocator>();
Debug.Assert(allocator != null);
var result = allocator.Allocate<PyUnicodeObject312>(value.Length * sizeof(char));
result._asciiObject.hash.Write(-1);
result._asciiObject.length.Write(value.Length);
result._compactObject.utf8_length.Write(value.Length);
var kind = value.Length != Encoding.UTF8.GetByteCount(value) ? PyUnicode_Kind.PyUnicode_2BYTE_KIND : PyUnicode_Kind.PyUnicode_1BYTE_KIND;
var state = new State {
interned = Interned.SSTATE_NOT_INTERNED,
kind = kind,
compact = true,
ascii = false,
ready = true
};
result._asciiObject.state.Write((byte)state);
ulong dataPtr = result.Address.OffsetBy(StructProxy.SizeOf<PyCompactUnicodeObject312>(process));
result._compactObject.utf8.Write(dataPtr);
process.WriteMemory(dataPtr, Encoding.UTF8.GetBytes(value));
return result;
}
public override string ToString() {
byte[] buf;
State state = (State)_asciiObject.state.Read();
if (state.ascii) {
state.kind = PyUnicode_Kind.PyUnicode_1BYTE_KIND;
}
int length = checked((int)_asciiObject.length.Read());
if (length == 0) {
return "";
}
ulong data;
if (!state.compact) {
data = GetFieldProxy(_fields.data).Read();
} else if (state.ascii) {
data = Address.OffsetBy(StructProxy.SizeOf<PyASCIIObject312>(Process));
} else {
data = Address.OffsetBy(StructProxy.SizeOf<PyCompactUnicodeObject312>(Process));
}
if (data == 0) {
return null;
}
buf = new byte[length * (int)state.kind];
Process.ReadMemory(data, DkmReadMemoryFlags.None, buf);
Encoding enc;
switch (state.kind) {
case PyUnicode_Kind.PyUnicode_1BYTE_KIND:
enc = _latin1;
break;
case PyUnicode_Kind.PyUnicode_2BYTE_KIND:
enc = Encoding.Unicode;
break;
case PyUnicode_Kind.PyUnicode_4BYTE_KIND:
enc = Encoding.UTF32;
break;
default:
Debug.Fail("Unsupported PyUnicode_Kind " + state.kind);
return null;
}
return enc.GetString(buf, 0, buf.Length);
}
}
[StructProxy(MinVersion = PythonLanguageVersion.V312, StructName = "PyASCIIObject")]
[PyType(MinVersion = PythonLanguageVersion.V312, VariableName = "PyASCII_Type")]
internal class PyASCIIObject312 : StructProxy {
public class Fields {
public StructField<SSizeTProxy> length;
public StructField<SSizeTProxy> hash;
public StructField<ByteProxy> state;
}
private readonly Fields _fields;
public PyASCIIObject312(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public SSizeTProxy length {
get { return GetFieldProxy(_fields.length); }
}
public SSizeTProxy hash {
get { return GetFieldProxy(_fields.hash); }
}
public ByteProxy state {
get { return GetFieldProxy(_fields.state); }
}
}
[StructProxy(MinVersion = PythonLanguageVersion.V312, StructName = "PyCompactUnicodeObject")]
[PyType(MinVersion = PythonLanguageVersion.V312, VariableName = "PyCompactUnicode_Type")]
internal class PyCompactUnicodeObject312 : StructProxy {
public class Fields {
public StructField<SSizeTProxy> utf8_length;
public StructField<PointerProxy> utf8;
}
private readonly Fields _fields;
public PyCompactUnicodeObject312(DkmProcess process, ulong address)
: base(process, address) {
InitializeStruct(this, out _fields);
}
public SSizeTProxy utf8_length {
get { return GetFieldProxy(_fields.utf8_length); }
}
public PointerProxy utf8 {
get { return GetFieldProxy(_fields.utf8); }
}
}
}

Просмотреть файл

@ -76,6 +76,9 @@ namespace Microsoft.PythonTools.Debugger.Concord {
case "38": return PythonLanguageVersion.V38;
case "39": return PythonLanguageVersion.V39;
case "310": return PythonLanguageVersion.V310;
case "311": return PythonLanguageVersion.V311;
case "312": return PythonLanguageVersion.V312;
case "313": return PythonLanguageVersion.V313;
default: return PythonLanguageVersion.None;
}
}

Просмотреть файл

@ -62,28 +62,48 @@ namespace Microsoft.PythonTools.Debugger.Concord {
// Layout of this struct must always remain in sync with DebuggerHelper/trace.cpp.
[StructLayout(LayoutKind.Sequential, Pack = 8)]
private struct PyCodeObject_FieldOffsets {
public readonly long co_varnames, co_filename, co_name;
public readonly long co_filename, co_name;
public PyCodeObject_FieldOffsets(DkmProcess process) {
var fields = StructProxy.GetStructFields<PyCodeObject, PyCodeObject.Fields>(process);
co_varnames = fields.co_varnames.Offset;
co_filename = fields.co_filename.Offset;
co_name = fields.co_name.Offset;
if (process.GetPythonRuntimeInfo().LanguageVersion <= PythonLanguageVersion.V310) {
var fields = StructProxy.GetStructFields<PyCodeObject310, PyCodeObject310.Fields>(process);
co_filename = fields.co_filename.Offset;
co_name = fields.co_name.Offset;
} else {
var fields = StructProxy.GetStructFields<PyCodeObject311, PyCodeObject311.Fields>(process);
co_filename = fields.co_filename.Offset;
co_name = fields.co_name.Offset;
}
}
}
// Layout of this struct must always remain in sync with DebuggerHelper/trace.cpp.
[StructLayout(LayoutKind.Sequential, Pack = 8)]
private struct PyFrameObject_FieldOffsets {
public readonly long f_back, f_code, f_globals, f_locals, f_lineno;
public readonly long f_back, f_code, f_globals, f_locals, f_lineno, f_frame;
public PyFrameObject_FieldOffsets(DkmProcess process) {
var fields = StructProxy.GetStructFields<PyFrameObject, PyFrameObject.Fields>(process);
f_back = fields.f_back.Offset;
f_code = fields.f_code.Offset;
f_globals = fields.f_globals.Offset;
f_locals = fields.f_locals.Offset;
f_lineno = fields.f_lineno.Offset;
// For 310, these are on the _frame struct itself.
if (process.GetPythonRuntimeInfo().LanguageVersion <= PythonLanguageVersion.V310) {
var fields = StructProxy.GetStructFields<PyFrameObject310, PyFrameObject310.Fields>(process);
f_back = fields.f_back.Offset;
f_code = fields.f_code.Offset;
f_globals = fields.f_globals.Offset;
f_locals = fields.f_locals.Offset;
f_lineno = fields.f_lineno.Offset;
f_frame = 0;
return;
}
// For 311 and higher, they are on the PyInterpreterFrame struct which is pointed to by the _frame struct.
var _frameFields = StructProxy.GetStructFields<PyFrameObject311, PyFrameObject311.Fields>(process);
var _interpreterFields = StructProxy.GetStructFields<PyInterpreterFrame, PyInterpreterFrame.Fields>(process);
f_frame = _frameFields.f_frame.Offset;
f_back = _frameFields.f_back.Offset;
f_code = _interpreterFields.f_code.Process != null ? _interpreterFields.f_code.Offset : _interpreterFields.f_executable.Offset;
f_globals = _interpreterFields.f_globals.Offset;
f_locals = _interpreterFields.f_locals.Offset;
f_lineno = _frameFields.f_lineno.Offset;
}
}
@ -102,22 +122,43 @@ namespace Microsoft.PythonTools.Debugger.Concord {
[StructLayout(LayoutKind.Sequential, Pack = 8)]
private struct PyUnicodeObject_FieldOffsets {
public readonly long sizeof_PyASCIIObject, sizeof_PyCompactUnicodeObject;
public readonly long length, state, wstr, wstr_length, data;
public readonly long length, state, wstr, wstr_length, utf8, utf8_length, data;
public PyUnicodeObject_FieldOffsets(DkmProcess process) {
sizeof_PyASCIIObject = StructProxy.SizeOf<PyASCIIObject>(process);
sizeof_PyCompactUnicodeObject = StructProxy.SizeOf<PyUnicodeObject>(process);
if (process.GetPythonRuntimeInfo().LanguageVersion <= PythonLanguageVersion.V311) {
sizeof_PyASCIIObject = StructProxy.SizeOf<PyASCIIObject311>(process);
sizeof_PyCompactUnicodeObject = StructProxy.SizeOf<PyCompactUnicodeObject311>(process);
var asciiFields = StructProxy.GetStructFields<PyASCIIObject, PyASCIIObject.Fields>(process);
length = asciiFields.length.Offset;
state = asciiFields.state.Offset;
wstr = asciiFields.wstr.Offset;
var asciiFields = StructProxy.GetStructFields<PyASCIIObject311, PyASCIIObject311.Fields>(process);
length = asciiFields.length.Offset;
state = asciiFields.state.Offset;
wstr = asciiFields.wstr.Offset;
utf8 = 0;
utf8_length = 0;
var compactFields = StructProxy.GetStructFields<PyCompactUnicodeObject, PyCompactUnicodeObject.Fields>(process);
wstr_length = compactFields.wstr_length.Offset;
var compactFields = StructProxy.GetStructFields<PyCompactUnicodeObject311, PyCompactUnicodeObject311.Fields>(process);
wstr_length = compactFields.wstr_length.Offset;
var unicodeFields = StructProxy.GetStructFields<PyUnicodeObject311, PyUnicodeObject311.Fields>(process);
data = unicodeFields.data.Offset;
} else {
sizeof_PyASCIIObject = StructProxy.SizeOf<PyASCIIObject312>(process);
sizeof_PyCompactUnicodeObject = StructProxy.SizeOf<PyCompactUnicodeObject312>(process);
var asciiFields = StructProxy.GetStructFields<PyASCIIObject312, PyASCIIObject312.Fields>(process);
length = asciiFields.length.Offset;
state = asciiFields.state.Offset;
wstr = 0;
var compactFields = StructProxy.GetStructFields<PyCompactUnicodeObject312, PyCompactUnicodeObject312.Fields>(process);
wstr_length = 0;
utf8_length = compactFields.utf8_length.Offset;
utf8 = compactFields.utf8.Offset;
var unicodeFields = StructProxy.GetStructFields<PyUnicodeObject312, PyUnicodeObject312.Fields>(process);
data = unicodeFields.data.Offset;
}
var unicodeFields = StructProxy.GetStructFields<PyUnicodeObject, PyUnicodeObject.Fields>(process);
data = unicodeFields.data.Offset;
}
}
@ -149,7 +190,9 @@ namespace Microsoft.PythonTools.Debugger.Concord {
public Types(DkmProcess process, PythonRuntimeInfo pyrtInfo) {
PyBytes_Type = PyObject.GetPyType<PyBytesObject>(process).Address;
PyUnicode_Type = PyObject.GetPyType<PyUnicodeObject>(process).Address;
PyUnicode_Type = process.GetPythonRuntimeInfo().LanguageVersion <= PythonLanguageVersion.V311 ?
PyObject.GetPyType<PyUnicodeObject311>(process).Address :
PyObject.GetPyType<PyUnicodeObject312>(process).Address;
}
}
@ -163,6 +206,11 @@ namespace Microsoft.PythonTools.Debugger.Concord {
public ulong PyErr_Restore;
public ulong PyErr_Occurred;
public ulong PyObject_Str;
public ulong PyEval_SetTraceAllThreads;
public ulong PyGILState_Ensure;
public ulong PyGILState_Release;
public ulong Py_Initialize;
public ulong Py_Finalize;
public FunctionPointers(DkmProcess process, PythonRuntimeInfo pyrtInfo) {
Py_DecRef = pyrtInfo.DLLs.Python.GetFunctionAddress("Py_DecRef");
@ -172,6 +220,13 @@ namespace Microsoft.PythonTools.Debugger.Concord {
PyErr_Restore = pyrtInfo.DLLs.Python.GetFunctionAddress("PyErr_Restore");
PyErr_Occurred = pyrtInfo.DLLs.Python.GetFunctionAddress("PyErr_Occurred");
PyObject_Str = pyrtInfo.DLLs.Python.GetFunctionAddress("PyObject_Str");
PyEval_SetTraceAllThreads = pyrtInfo.LanguageVersion >= PythonLanguageVersion.V312 ?
pyrtInfo.DLLs.Python.GetFunctionAddress("PyEval_SetTraceAllThreads") :
0;
PyGILState_Ensure = pyrtInfo.DLLs.Python.GetFunctionAddress("PyGILState_Ensure");
PyGILState_Release = pyrtInfo.DLLs.Python.GetFunctionAddress("PyGILState_Release");
Py_Initialize = pyrtInfo.DLLs.Python.GetFunctionAddress("Py_Initialize");
Py_Finalize = pyrtInfo.DLLs.Python.GetFunctionAddress("Py_Finalize");
}
}
@ -180,6 +235,7 @@ namespace Microsoft.PythonTools.Debugger.Concord {
private readonly PythonDllBreakpointHandlers _handlers;
private readonly DkmNativeInstructionAddress _traceFunc;
private readonly DkmNativeInstructionAddress _evalFrameFunc;
private readonly DkmNativeInstructionAddress _pyEval_FrameDefault;
private readonly PointerProxy _defaultEvalFrameFunc;
private readonly ByteProxy _isTracing;
@ -222,6 +278,7 @@ namespace Microsoft.PythonTools.Debugger.Concord {
_process = process;
_pyrtInfo = process.GetPythonRuntimeInfo();
_pyEval_FrameDefault = _pyrtInfo.DLLs.Python.GetExportedFunctionAddress("_PyEval_EvalFrameDefault");
_traceFunc = _pyrtInfo.DLLs.DebuggerHelper.GetExportedFunctionAddress("TraceFunc");
_evalFrameFunc =
_pyrtInfo.DLLs.DebuggerHelper.GetExportedFunctionAddress("EvalFrameFunc");
@ -243,7 +300,7 @@ namespace Microsoft.PythonTools.Debugger.Concord {
if (_pyrtInfo.LanguageVersion >= PythonLanguageVersion.V36) {
RegisterJITTracing(interp);
}
foreach (var tstate in interp.GetThreadStates()) {
foreach (var tstate in interp.GetThreadStates(process)) {
RegisterTracing(tstate);
}
}
@ -251,6 +308,7 @@ namespace Microsoft.PythonTools.Debugger.Concord {
_handlers = new PythonDllBreakpointHandlers(this);
LocalComponent.CreateRuntimeDllFunctionExitBreakpoints(_pyrtInfo.DLLs.Python, "new_threadstate", _handlers.new_threadstate, enable: true);
LocalComponent.CreateRuntimeDllFunctionExitBreakpoints(_pyrtInfo.DLLs.Python, "PyInterpreterState_New", _handlers.PyInterpreterState_New, enable: true);
LocalComponent.CreateRuntimeDllFunctionExitBreakpoints(_pyrtInfo.DLLs.Python, "_PyInterpreterState_New", _handlers._PyInterpreterState_New, enable: true);
foreach (var methodInfo in _handlers.GetType().GetMethods()) {
var stepInAttr = (StepInGateAttribute)Attribute.GetCustomAttribute(methodInfo, typeof(StepInGateAttribute));
@ -269,9 +327,15 @@ namespace Microsoft.PythonTools.Debugger.Concord {
}
}
private IEnumerable<Int32Proxy> GetTracingPossible(PythonRuntimeInfo pyrtInfo, DkmProcess process) =>
from interp in PyInterpreterState.GetInterpreterStates(process)
private IEnumerable<Int32Proxy> GetTracingPossible(PythonRuntimeInfo pyrtInfo, DkmProcess process) {
// On 3.10 and above the tracing_possible is determined on each thread by the cframe object, so we don't need to
// check the interpreter state (it's no longer set there).
if (pyrtInfo.LanguageVersion > PythonLanguageVersion.V39) {
return Enumerable.Empty<Int32Proxy>();
}
return from interp in PyInterpreterState.GetInterpreterStates(process)
select interp.ceval.tracing_possible;
}
private void AddStepInGate(StepInGateHandler handler, DkmNativeModuleInstance module, string funcName, bool hasMultipleExitPoints) {
var gate = new StepInGate {
@ -288,8 +352,7 @@ namespace Microsoft.PythonTools.Debugger.Concord {
}
public unsafe void RegisterTracing(PyThreadState tstate) {
tstate.use_tracing.Write(1);
tstate.c_tracefunc.Write(_traceFunc.GetPointer());
tstate.RegisterTracing(_traceFunc.GetPointer());
foreach (var pyTracingPossible in GetTracingPossible(_pyrtInfo, _process)) {
pyTracingPossible.Write(pyTracingPossible.Read() + 1);
}
@ -300,9 +363,16 @@ namespace Microsoft.PythonTools.Debugger.Concord {
Debug.Assert(_pyrtInfo.LanguageVersion >= PythonLanguageVersion.V36);
var current = istate.eval_frame.Read();
if (current != _evalFrameFunc.GetPointer()) {
var evalFrameAddr = _evalFrameFunc.GetPointer();
if (current == 0) {
// This means the eval_frame is set to the default. Write
// this as our _defaultEvalFrameFunc
_defaultEvalFrameFunc.Write(_pyEval_FrameDefault.GetPointer());
istate.eval_frame.Write(evalFrameAddr);
} else if (current != evalFrameAddr) {
_defaultEvalFrameFunc.Write(current);
istate.eval_frame.Write(_evalFrameFunc.GetPointer());
istate.eval_frame.Write(evalFrameAddr);
}
}
@ -481,6 +551,15 @@ namespace Microsoft.PythonTools.Debugger.Concord {
}
}
public void _PyInterpreterState_New(DkmThread thread, ulong frameBase, ulong vframe, ulong returnAddress) {
// The new interpreter should be the 'head' of the list of interpreters
// (this function actually returns a status code, not the interpreter state).
var head = PyInterpreterState.GetInterpreterStates(thread.Process).First();
if (head != null && head.Process != null) {
_owner.RegisterJITTracing(head);
}
}
// This step-in gate is not marked [StepInGate] because it doesn't live in pythonXX.dll, and so we register it manually.
public void _call_function_pointer(DkmThread thread, ulong frameBase, ulong vframe, bool useRegisters) {
var cppEval = new CppExpressionEvaluator(thread, frameBase, vframe);
@ -488,7 +567,7 @@ namespace Microsoft.PythonTools.Debugger.Concord {
_owner.OnPotentialRuntimeExit(thread, pProc);
}
[StepInGate]
[StepInGate(MaxVersion = PythonLanguageVersion.V310)]
public void call_function(DkmThread thread, ulong frameBase, ulong vframe, bool useRegisters) {
var process = thread.Process;
var cppEval = new CppExpressionEvaluator(thread, frameBase, vframe);
@ -511,6 +590,19 @@ namespace Microsoft.PythonTools.Debugger.Concord {
_owner.OnPotentialRuntimeExit(thread, ml_meth);
}
[StepInGate(MinVersion = PythonLanguageVersion.V311)]
public void PyObject_Vectorcall(DkmThread thread, ulong frameBase, ulong vframe, bool useRegisters) {
var process = thread.Process;
var cppEval = new CppExpressionEvaluator(thread, frameBase, vframe);
ulong func = cppEval.EvaluateUInt64(useRegisters ? "@rdx" : "callable");
var obj = PyObject.FromAddress(process, func);
ulong ml_meth = cppEval.EvaluateUInt64(
"((PyObject*){0})->ob_type == &PyCFunction_Type ? ((PyCFunctionObject*){0})->m_ml->ml_meth : 0",
func);
_owner.OnPotentialRuntimeExit(thread, ml_meth);
}
[StepInGate]
public void PyCFunction_Call(DkmThread thread, ulong frameBase, ulong vframe, bool useRegisters) {
var process = thread.Process;

Просмотреть файл

@ -51,17 +51,17 @@ struct {
int64_t ob_size;
} PyVarObject;
struct {
int64_t f_back, f_code, f_globals, f_locals, f_lineno;
int64_t f_back, f_code, f_globals, f_locals, f_lineno, f_frame;
} PyFrameObject;
struct {
int64_t co_varnames, co_filename, co_name;
int64_t co_filename, co_name;
} PyCodeObject;
struct {
int64_t ob_sval;
} PyBytesObject;
struct {
int64_t PyAsciiObjectData, PyCompactUnicodeObjectData;
int64_t length, state, wstr, wstr_length, data;
int64_t sizeof_PyAsciiObjectData, sizeof_PyCompactUnicodeObjectData;
int64_t length, state, wstr, wstr_length, utf8, utf8_length, data;
} PyUnicodeObject;
} fieldOffsets;
@ -82,6 +82,11 @@ struct {
uint64_t PyErr_Restore;
uint64_t PyErr_Occurred;
uint64_t PyObject_Str;
uint64_t PyEval_SetTraceAllThreads;
uint64_t PyGILState_Ensure;
uint64_t PyGILState_Release;
uint64_t Py_Initialize;
uint64_t Py_Finalize;
} functionPointers;
void Py_DecRef(PyObject* a1) {
@ -112,6 +117,33 @@ PyObject* PyObject_Str(PyObject* o) {
return reinterpret_cast<decltype(&PyObject_Str)>(functionPointers.PyObject_Str)(o);
}
/* Py_tracefunc return -1 when raising an exception, or 0 for success. */
typedef int (*Py_tracefunc)(void* obj, void* frame, int what, void* arg);
void
PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject* arg) {
return reinterpret_cast<decltype(&PyEval_SetTraceAllThreads)>(functionPointers.PyEval_SetTraceAllThreads)(func, arg);
}
typedef
enum { PyGILState_LOCKED, PyGILState_UNLOCKED } PyGILState_STATE;
PyGILState_STATE PyGILState_Ensure() {
return reinterpret_cast<decltype(&PyGILState_Ensure)>(functionPointers.PyGILState_Ensure)();
}
void PyGILState_Release(PyGILState_STATE state) {
return reinterpret_cast<decltype(&PyGILState_Release)>(functionPointers.PyGILState_Release)(state);
}
void Py_Initialize() {
return reinterpret_cast<decltype(&Py_Initialize)>(functionPointers.Py_Initialize)();
}
void Py_Finalize() {
return reinterpret_cast<decltype(&Py_Finalize)>(functionPointers.Py_Finalize)();
}
// A string provided by the debugger (e.g. for file names). This is actually a variable-length struct,
// with _countof(data) == length + 1 - the extra wchar_t is the null terminator.
struct DebuggerString {
@ -256,7 +288,7 @@ bool StringEquals(const DebuggerString* debuggerString, const void* pyString) {
state = ReadField<char>(pyString, fieldOffsets.PyUnicodeObject.state);
if (!ready) {
if (!ready && fieldOffsets.PyUnicodeObject.wstr != 0) {
auto wstr = ReadField<wchar_t*>(pyString, fieldOffsets.PyUnicodeObject.wstr);
if (!wstr) {
return false;
@ -279,9 +311,9 @@ bool StringEquals(const DebuggerString* debuggerString, const void* pyString) {
if (!compact) {
data = ReadField<void*>(pyString, fieldOffsets.PyUnicodeObject.data);
} else if (ascii) {
data = reinterpret_cast<const char*>(pyString) + fieldOffsets.PyUnicodeObject.PyAsciiObjectData;
data = reinterpret_cast<const char*>(pyString) + fieldOffsets.PyUnicodeObject.sizeof_PyAsciiObjectData;
} else {
data = reinterpret_cast<const char*>(pyString) + fieldOffsets.PyUnicodeObject.PyCompactUnicodeObjectData;
data = reinterpret_cast<const char*>(pyString) + fieldOffsets.PyUnicodeObject.sizeof_PyCompactUnicodeObjectData;
}
if (kind == 2) {
@ -353,8 +385,20 @@ void EvalLoop(void (*bp)()) {
auto frame = reinterpret_cast<PyObject*>(evalLoopFrame);
PyFrame_FastToLocals(frame);
auto f_globals = ReadField<PyObject*>(frame, fieldOffsets.PyFrameObject.f_globals);
auto f_locals = ReadField<PyObject*>(frame, fieldOffsets.PyFrameObject.f_locals);
PyObject* f_globals = NULL;
PyObject* f_locals = NULL;
if (fieldOffsets.PyFrameObject.f_frame == 0) {
// We're on 3.10 or earlier. f_globals and f_locals is directly off of the frame.
f_globals = ReadField<PyObject*>(frame, fieldOffsets.PyFrameObject.f_globals);
f_locals = ReadField<PyObject*>(frame, fieldOffsets.PyFrameObject.f_locals);
}
else {
// We're on 3.11 or later. f_frame (PyInterpreterFrame) is off of the frame. It has
// the f_globals and f_locals object.
void* f_frame = ReadField<void*>(frame, fieldOffsets.PyFrameObject.f_frame);
f_globals = ReadField<PyObject*>(f_frame, fieldOffsets.PyFrameObject.f_globals);
f_locals = ReadField<PyObject*>(f_frame, fieldOffsets.PyFrameObject.f_locals);
}
PyObject *orig_exc_type, *orig_exc_value, *orig_exc_tb;
PyErr_Fetch(&orig_exc_type, &orig_exc_value, &orig_exc_tb);
@ -425,7 +469,7 @@ static void TraceLine(void* frame) {
const auto& bpData = breakpointData[iData];
int f_lineno = ReadField<int>(frame, fieldOffsets.PyFrameObject.f_lineno);
if (f_lineno > bpData.maxLineNumber) {
if (f_lineno > bpData.maxLineNumber || f_lineno <= 0) {
return;
}
@ -435,8 +479,19 @@ static void TraceLine(void* frame) {
return;
}
void* f_code = ReadField<void*>(frame, fieldOffsets.PyFrameObject.f_code);
void* co_filename = ReadField<void*>(f_code, fieldOffsets.PyCodeObject.co_filename);
void* co_filename = nullptr;
if (fieldOffsets.PyFrameObject.f_frame == 0) {
// We're on 3.10 or earlier. f_code is directly off of the frame.
void* f_code = ReadField<void*>(frame, fieldOffsets.PyFrameObject.f_code);
co_filename = ReadField<void*>(f_code, fieldOffsets.PyCodeObject.co_filename);
}
else {
// We're on 3.11 or later. f_frame (PyInterpreterFrame) is off of the frame. It has
// the f_code object.
void* f_frame = ReadField<void*>(frame, fieldOffsets.PyFrameObject.f_frame);
void* f_code = ReadField<void*>(f_frame, fieldOffsets.PyFrameObject.f_code);
co_filename = ReadField<void*>(f_code, fieldOffsets.PyCodeObject.co_filename);
}
if (co_filename == nullptr) {
return;
}
@ -540,19 +595,46 @@ int TraceFunc(void* obj, void* frame, int what, void* arg) {
return 0;
}
// In Python 3.9 the eval function added the thread state. So
// we need a new function def to accomodate that change.
typedef void* (*_PyFrameEvalFunction)(void*,void*, int);
void* InitialEvalFrameFunc(void* ts, void* f, int throwFlag);
typedef void* (*_PyFrameEvalFunction)(void*, void*, int);
_PyFrameEvalFunction CurrentEvalFrameFunc = InitialEvalFrameFunc;
__declspec(dllexport)
_PyFrameEvalFunction DefaultEvalFrameFunc = nullptr;
__declspec(dllexport)
void* EvalFrameFunc(void* ts, void* f, int throwFlag)
// Initial EvalFrameFunc that is used to set the trace function.
volatile unsigned long _isTracing = 0;
void *InitialEvalFrameFunc(void* ts, void* f, int throwFlag)
{
// If were in 3.12, we need to set the trace function ourselves.
// This is because just writing to the use_tracing flag is no longer enough. Internally CPython
// doesn't just trace everything if the flag is set. Instead tracing now uses the sys.monitoring
// api under the covers. That's a lot more than just flipping a flag.
if (functionPointers.PyEval_SetTraceAllThreads != 0 && ::InterlockedCompareExchange(&_isTracing, 1, 0) == 0) {
auto gilState = PyGILState_Ensure();
PyEval_SetTraceAllThreads(TraceFunc, nullptr);
PyGILState_Release(gilState);
}
// Rewrite the current EvalFrameFunc so we don't bother attempting to register the trace function again.
CurrentEvalFrameFunc = DefaultEvalFrameFunc;
if (DefaultEvalFrameFunc)
return (*DefaultEvalFrameFunc)(ts, f, throwFlag);
return nullptr;
}
// Function that is inserted into the current thread state by the debugger as the function to call
// in order to evaluate a frame. This is done so that the debugger can intercept the call
__declspec(dllexport)
void* EvalFrameFunc(void* ts, void* f, int throwFlag)
{
return (*CurrentEvalFrameFunc)(ts, f, throwFlag);
}
}

Просмотреть файл

@ -46,6 +46,9 @@ namespace TestUtilities {
public static readonly PythonVersion Python38 = GetCPythonVersion(PythonLanguageVersion.V38, InterpreterArchitecture.x86);
public static readonly PythonVersion Python39 = GetCPythonVersion(PythonLanguageVersion.V39, InterpreterArchitecture.x86);
public static readonly PythonVersion Python310 = GetCPythonVersion(PythonLanguageVersion.V310, InterpreterArchitecture.x86);
public static readonly PythonVersion Python311 = GetCPythonVersion(PythonLanguageVersion.V311, InterpreterArchitecture.x86);
public static readonly PythonVersion Python312 = GetCPythonVersion(PythonLanguageVersion.V312, InterpreterArchitecture.x86);
public static readonly PythonVersion Python313 = GetCPythonVersion(PythonLanguageVersion.V313, InterpreterArchitecture.x86);
public static readonly PythonVersion Python27_x64 = GetCPythonVersion(PythonLanguageVersion.V27, InterpreterArchitecture.x64);
public static readonly PythonVersion Python35_x64 = GetCPythonVersion(PythonLanguageVersion.V35, InterpreterArchitecture.x64);
public static readonly PythonVersion Python36_x64 = GetCPythonVersion(PythonLanguageVersion.V36, InterpreterArchitecture.x64);
@ -53,6 +56,9 @@ namespace TestUtilities {
public static readonly PythonVersion Python38_x64 = GetCPythonVersion(PythonLanguageVersion.V38, InterpreterArchitecture.x64);
public static readonly PythonVersion Python39_x64 = GetCPythonVersion(PythonLanguageVersion.V39, InterpreterArchitecture.x64);
public static readonly PythonVersion Python310_x64 = GetCPythonVersion(PythonLanguageVersion.V310, InterpreterArchitecture.x64);
public static readonly PythonVersion Python311_x64 = GetCPythonVersion(PythonLanguageVersion.V311, InterpreterArchitecture.x64);
public static readonly PythonVersion Python312_x64 = GetCPythonVersion(PythonLanguageVersion.V312, InterpreterArchitecture.x64);
public static readonly PythonVersion Python313_x64 = GetCPythonVersion(PythonLanguageVersion.V313, InterpreterArchitecture.x64);
public static readonly PythonVersion Anaconda27 = GetAnacondaVersion(PythonLanguageVersion.V27, InterpreterArchitecture.x86);
public static readonly PythonVersion Anaconda27_x64 = GetAnacondaVersion(PythonLanguageVersion.V27, InterpreterArchitecture.x64);
public static readonly PythonVersion Anaconda36 = GetAnacondaVersion(PythonLanguageVersion.V36, InterpreterArchitecture.x86);

Просмотреть файл

@ -2,6 +2,6 @@
"name": "ptvs",
"private": true,
"devDependencies": {
"@pylance/pylance": "2024.11.1"
"@pylance/pylance": "2024.11.2"
}
}