PageRenderTime 23ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/Python/Product/Debugger/DkmDebugger/ModuleManager.cs

https://gitlab.com/SplatoonModdingHub/PTVS
C# | 177 lines | 133 code | 22 blank | 22 comment | 25 complexity | 5229a5c6c39140655fec7dc09890a702 MD5 | raw file
  1. // Python Tools for Visual Studio
  2. // Copyright(c) Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the License); you may not use
  6. // this file except in compliance with the License. You may obtain a copy of the
  7. // License at http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
  10. // OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
  11. // IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  12. // MERCHANTABLITY OR NON-INFRINGEMENT.
  13. //
  14. // See the Apache Version 2.0 License for specific language governing
  15. // permissions and limitations under the License.
  16. using System;
  17. using System.IO;
  18. using System.Linq;
  19. using Microsoft.PythonTools.Debugger;
  20. using Microsoft.PythonTools.DkmDebugger.Proxies;
  21. using Microsoft.PythonTools.DkmDebugger.Proxies.Structs;
  22. using Microsoft.VisualStudio.Debugger;
  23. using Microsoft.VisualStudio.Debugger.CustomRuntimes;
  24. using Microsoft.VisualStudio.Debugger.Evaluation;
  25. using Microsoft.VisualStudio.Debugger.Symbols;
  26. namespace Microsoft.PythonTools.DkmDebugger {
  27. internal class ModuleManager : DkmDataItem {
  28. public static DkmResolvedDocument[] FindDocuments(DkmModule module, DkmSourceFileId sourceFileId) {
  29. DkmDocumentMatchStrength matchStrength;
  30. if (string.Equals(module.Name, sourceFileId.DocumentName, StringComparison.OrdinalIgnoreCase)) {
  31. matchStrength = DkmDocumentMatchStrength.FullPath;
  32. } else {
  33. // Either the module path is relative, or it's absolute but on a different filesystem (i.e. remote debugging).
  34. // Walk the local filesystem up starting from source file path, matching it against the module path component
  35. // by component, stopping once __init__.py is no longer seen on the same level. The intent is to approximate
  36. // a match on module names by matching the tails of the two paths that contribute to the fully qualified names
  37. // of the modules.
  38. string sourcePath = sourceFileId.DocumentName;
  39. string modulePath = module.Name;
  40. int levels = 0;
  41. do {
  42. try {
  43. string sourceFile = Path.GetFileName(sourcePath);
  44. string moduleFile = Path.GetFileName(modulePath);
  45. if (!string.Equals(sourceFile, moduleFile, StringComparison.OrdinalIgnoreCase)) {
  46. return new DkmResolvedDocument[0];
  47. }
  48. sourcePath = Path.GetDirectoryName(sourcePath);
  49. modulePath = Path.GetDirectoryName(modulePath);
  50. } catch (ArgumentException) {
  51. return new DkmResolvedDocument[0];
  52. }
  53. ++levels;
  54. } while (File.Exists(Path.Combine(sourcePath, "__init__.py")));
  55. matchStrength = (levels == 1) ? DkmDocumentMatchStrength.FileName : DkmDocumentMatchStrength.SubPath;
  56. }
  57. return new[] {
  58. DkmResolvedDocument.Create(module, module.Name, null, matchStrength, DkmResolvedDocumentWarning.None, false, null)
  59. };
  60. }
  61. private readonly DkmProcess _process;
  62. private readonly PythonRuntimeInfo _pyrtInfo;
  63. public ModuleManager(DkmProcess process) {
  64. _process = process;
  65. _pyrtInfo = process.GetPythonRuntimeInfo();
  66. LoadInitialPythonModules();
  67. LocalComponent.CreateRuntimeDllFunctionBreakpoint(_pyrtInfo.DLLs.Python, "PyCode_New", PythonDllBreakpointHandlers.PyCode_New, enable: true, debugStart: true);
  68. LocalComponent.CreateRuntimeDllFunctionBreakpoint(_pyrtInfo.DLLs.Python, "PyCode_NewEmpty", PythonDllBreakpointHandlers.PyCode_NewEmpty, enable: true, debugStart: true);
  69. }
  70. private void LoadInitialPythonModules() {
  71. foreach (var interp in PyInterpreterState.GetInterpreterStates(_process)) {
  72. var modules = interp.modules.TryRead();
  73. if (modules == null) {
  74. continue;
  75. }
  76. foreach (var moduleEntry in modules.ReadElements()) {
  77. var module = moduleEntry.Value.TryRead() as PyModuleObject;
  78. if (module == null) {
  79. continue;
  80. }
  81. var md_dict = module.md_dict.TryRead();
  82. if (md_dict == null) {
  83. continue;
  84. }
  85. foreach (var entry in md_dict.ReadElements()) {
  86. var name = (entry.Key as IPyBaseStringObject).ToStringOrNull();
  87. if (name == "__file__") {
  88. var fileName = (entry.Value.TryRead() as IPyBaseStringObject).ToStringOrNull();
  89. if (fileName != null && !fileName.EndsWith(".pyd")) {
  90. // Unlike co_filename, __file__ usually reflects the actual name of the file from which the module
  91. // was created, which will be .pyc rather than .py if it was available, so fix that up.
  92. if (fileName.EndsWith(".pyc")) {
  93. fileName = fileName.Substring(0, fileName.Length - 1);
  94. }
  95. new RemoteComponent.CreateModuleRequest {
  96. ModuleId = Guid.NewGuid(),
  97. FileName = fileName
  98. }.SendLower(_process);
  99. }
  100. }
  101. }
  102. }
  103. }
  104. }
  105. public static DkmInstructionSymbol[] FindSymbols(DkmResolvedDocument resolvedDocument, DkmTextSpan textSpan, string text, out DkmSourcePosition[] symbolLocation) {
  106. var sourceFileId = DkmSourceFileId.Create(resolvedDocument.DocumentName, null, null, null);
  107. var resultSpan = new DkmTextSpan(textSpan.StartLine, textSpan.StartLine, 0, 0);
  108. symbolLocation = new[] { DkmSourcePosition.Create(sourceFileId, resultSpan) };
  109. var location = new SourceLocation(resolvedDocument.DocumentName, textSpan.StartLine);
  110. var encodedLocation = location.Encode();
  111. return new[] { DkmCustomInstructionSymbol.Create(resolvedDocument.Module, Guids.PythonRuntimeTypeGuid, encodedLocation, 0, encodedLocation) };
  112. }
  113. public static DkmSourcePosition GetSourcePosition(DkmInstructionSymbol instruction, DkmSourcePositionFlags flags, DkmInspectionSession inspectionSession, out bool startOfLine) {
  114. var insSym = instruction as DkmCustomInstructionSymbol;
  115. var loc = new SourceLocation(insSym.AdditionalData);
  116. startOfLine = true;
  117. return DkmSourcePosition.Create(DkmSourceFileId.Create(loc.FileName, null, null, null), new DkmTextSpan(loc.LineNumber, loc.LineNumber, 0, 0));
  118. }
  119. private class PythonDllBreakpointHandlers {
  120. public static void PyCode_New(DkmThread thread, ulong frameBase, ulong vframe, ulong returnAddress) {
  121. var process = thread.Process;
  122. var cppEval = new CppExpressionEvaluator(thread, frameBase, vframe);
  123. var filenamePtr = cppEval.EvaluateUInt64("filename");
  124. var filenameObj = PyObject.FromAddress(process, filenamePtr) as IPyBaseStringObject;
  125. if (filenameObj == null) {
  126. return;
  127. }
  128. string filename = filenameObj.ToString();
  129. if (process.GetPythonRuntimeInstance().GetModuleInstances().Any(mi => mi.FullName == filename)) {
  130. return;
  131. }
  132. new RemoteComponent.CreateModuleRequest {
  133. ModuleId = Guid.NewGuid(),
  134. FileName = filename
  135. }.SendLower(process);
  136. }
  137. public static void PyCode_NewEmpty(DkmThread thread, ulong frameBase, ulong vframe, ulong returnAddress) {
  138. var process = thread.Process;
  139. var cppEval = new CppExpressionEvaluator(thread, frameBase, vframe);
  140. ulong filenamePtr = cppEval.EvaluateUInt64("filename");
  141. if (filenamePtr == 0) {
  142. return;
  143. }
  144. string filename = new CStringProxy(process, filenamePtr).ReadUnicode();
  145. if (process.GetPythonRuntimeInstance().GetModuleInstances().Any(mi => mi.FullName == filename)) {
  146. return;
  147. }
  148. new RemoteComponent.CreateModuleRequest {
  149. ModuleId = Guid.NewGuid(),
  150. FileName = filename
  151. }.SendLower(process);
  152. }
  153. }
  154. }
  155. }