1
1
using System ;
2
2
using System . Collections . Generic ;
3
+ using System . Diagnostics ;
3
4
using System . Globalization ;
4
5
using System . IO ;
6
+ using Microsoft . VisualStudio ;
7
+ using Microsoft . VisualStudio . Debugger ;
5
8
using Microsoft . VisualStudio . Debugger . CallStack ;
6
9
using Microsoft . VisualStudio . Debugger . ComponentInterfaces ;
10
+ using Microsoft . VisualStudio . Shell ;
11
+ using Microsoft . VisualStudio . Shell . Interop ;
7
12
8
13
namespace PmipMyCallStack
9
14
{
10
- public class PmipCallStackFilter : IDkmCallStackFilter
15
+ public class PmipCallStackFilter : IDkmCallStackFilter , IDkmLoadCompleteNotification
11
16
{
12
- private static Range [ ] _rangesSortedByIp ;
13
- private static long _previousFileLength ;
17
+ private static List < Range > _rangesSortedByIp = new List < Range > ( ) ;
14
18
private static FuzzyRangeComparer _comparer = new FuzzyRangeComparer ( ) ;
19
+ private static bool _enabled = true ;
20
+ private static IVsOutputWindowPane _debugPane ;
21
+ private static string _currentFile ;
22
+ private static FileStream _fileStream ;
23
+ private static StreamReader _fileStreamReader ;
24
+
25
+ public void OnLoadComplete ( DkmProcess process , DkmWorkList workList , DkmEventDescriptor eventDescriptor )
26
+ {
27
+ IVsOutputWindow outWindow = Package . GetGlobalService ( typeof ( SVsOutputWindow ) ) as IVsOutputWindow ;
28
+ Guid debugPaneGuid = VSConstants . GUID_OutWindowDebugPane ;
29
+ outWindow . GetPane ( ref debugPaneGuid , out _debugPane ) ;
30
+
31
+ var env = Environment . GetEnvironmentVariable ( "UNITY_MIXED_CALLSTACK" ) ;
32
+ if ( env == null || env == "0" ) // plugin not enabled
33
+ {
34
+ _debugPane . OutputString ( "Warning: To use the UnityMixedCallstack plugin please set the environment variable UNITY_MIXED_CALLSTACK=1 and relaunch Unity and Visual Studio\n " ) ;
35
+ _debugPane . Activate ( ) ;
36
+ _enabled = false ;
37
+ }
38
+ }
15
39
16
40
public DkmStackWalkFrame [ ] FilterNextFrame ( DkmStackContext stackContext , DkmStackWalkFrame input )
17
41
{
@@ -27,14 +51,15 @@ public DkmStackWalkFrame[] FilterNextFrame(DkmStackContext stackContext, DkmStac
27
51
if ( ! stackContext . Thread . IsMainThread ) // error case
28
52
return new [ ] { input } ;
29
53
54
+ if ( ! _enabled ) // environment variable not set
55
+ return new [ ] { input } ;
30
56
31
57
return new [ ] { PmipStackFrame ( stackContext , input ) } ;
32
58
}
33
59
34
- public static DkmStackWalkFrame PmipStackFrame ( DkmStackContext stackContext , DkmStackWalkFrame frame )
60
+ private static DkmStackWalkFrame PmipStackFrame ( DkmStackContext stackContext , DkmStackWalkFrame frame )
35
61
{
36
- var fileName = Path . Combine ( Path . GetTempPath ( ) , "pmip." + frame . Process . LivePart . Id ) ;
37
- RefreshStackData ( fileName ) ;
62
+ RefreshStackData ( frame . Process . LivePart . Id ) ;
38
63
string name = null ;
39
64
if ( TryGetDescriptionForIp ( frame . InstructionAddress . CPUInstructionPart . InstructionPointer , out name ) )
40
65
return DkmStackWalkFrame . Create (
@@ -50,70 +75,105 @@ public static DkmStackWalkFrame PmipStackFrame(DkmStackContext stackContext, Dkm
50
75
return frame ;
51
76
}
52
77
53
- public static void RefreshStackData ( string fileName )
78
+ private static int GetFileNameSequenceNum ( string path )
54
79
{
55
- try
80
+ var name = Path . GetFileNameWithoutExtension ( path ) ;
81
+ const char delemiter = '_' ;
82
+ var tokens = name . Split ( delemiter ) ;
83
+
84
+ if ( tokens . Length != 3 )
85
+ return - 1 ;
86
+
87
+ return int . Parse ( tokens [ 2 ] ) ;
88
+ }
89
+
90
+ private static void DisposeStreams ( )
91
+ {
92
+ _fileStreamReader ? . Dispose ( ) ;
93
+ _fileStream ? . Dispose ( ) ;
94
+ _rangesSortedByIp . Clear ( ) ;
95
+ }
96
+
97
+ private static void RefreshStackData ( int pid )
98
+ {
99
+ DirectoryInfo taskDirectory = new DirectoryInfo ( Path . GetTempPath ( ) ) ;
100
+ FileInfo [ ] taskFiles = taskDirectory . GetFiles ( "pmip_" + pid + "_*.txt" ) ;
101
+
102
+ if ( taskFiles . Length < 1 )
103
+ return ;
104
+
105
+ Array . Sort ( taskFiles , ( a , b ) => GetFileNameSequenceNum ( a . Name ) . CompareTo ( GetFileNameSequenceNum ( b . Name ) ) ) ;
106
+ var fileName = taskFiles [ taskFiles . Length - 1 ] . FullName ;
107
+
108
+ if ( _currentFile != fileName )
56
109
{
57
- if ( ! File . Exists ( fileName ) )
58
- return ;
110
+ DisposeStreams ( ) ;
111
+ try
112
+ {
113
+ _fileStream = new FileStream ( fileName , FileMode . Open , FileAccess . Read , FileShare . ReadWrite | FileShare . Delete ) ;
114
+ _fileStreamReader = new StreamReader ( _fileStream ) ;
115
+ _currentFile = fileName ;
116
+ var versionStr = _fileStreamReader . ReadLine ( ) ;
117
+ const char delimiter = ':' ;
118
+ var tokens = versionStr . Split ( delimiter ) ;
59
119
60
- var fileInfo = new FileInfo ( fileName ) ;
61
- if ( fileInfo . Length == _previousFileLength )
62
- return ;
120
+ if ( tokens . Length != 2 )
121
+ throw new Exception ( "Failed reading input file " + fileName + ": Incorrect format" ) ;
122
+
123
+ var version = double . Parse ( tokens [ 1 ] ) ;
63
124
64
- var list = new List < Range > ( 10000 ) ;
65
- using ( var inStream = new FileStream ( fileName , FileMode . Open , FileAccess . Read , FileShare . ReadWrite ) )
125
+ if ( version > 1.0 )
126
+ throw new Exception ( "Failed reading input file " + fileName + ": A newer version of UnityMixedCallstacks plugin is required to read this file" ) ;
127
+ }
128
+ catch ( Exception ex )
66
129
{
67
- using ( var file = new StreamReader ( inStream ) )
68
- {
69
- string line ;
70
- while ( ( line = file . ReadLine ( ) ) != null )
71
- {
72
- const char delemiter = ';' ;
73
- var tokens = line . Split ( delemiter ) ;
74
-
75
- //should never happen, but lets be safe and not get array out of bounds if it does
76
- if ( tokens . Length != 3 )
77
- continue ;
78
-
79
- var startip = tokens [ 0 ] ;
80
- var endip = tokens [ 1 ] ;
81
- var description = tokens [ 2 ] ;
82
-
83
- var startiplong = ulong . Parse ( startip , NumberStyles . HexNumber ) ;
84
- var endipint = ulong . Parse ( endip , NumberStyles . HexNumber ) ;
85
-
86
- list . Add ( new Range ( ) { Name = description , Start = startiplong , End = endipint } ) ;
87
- }
88
- }
130
+ _currentFile = null ;
131
+ _debugPane . OutputString ( "Unable to read dumped pmip file: " + ex . Message + "\n " ) ;
132
+ DisposeStreams ( ) ;
133
+ return ;
89
134
}
135
+ }
136
+
137
+ try
138
+ {
139
+ string line ;
140
+ while ( ( line = _fileStreamReader . ReadLine ( ) ) != null )
141
+ {
142
+ const char delemiter = ';' ;
143
+ var tokens = line . Split ( delemiter ) ;
144
+
145
+ //should never happen, but lets be safe and not get array out of bounds if it does
146
+ if ( tokens . Length != 3 )
147
+ continue ;
148
+
149
+ var startip = tokens [ 0 ] ;
150
+ var endip = tokens [ 1 ] ;
151
+ var description = tokens [ 2 ] ;
90
152
91
- list . Sort ( ( r1 , r2 ) => r1 . Start . CompareTo ( r2 . Start ) ) ;
92
- _rangesSortedByIp = list . ToArray ( ) ;
93
- _previousFileLength = fileInfo . Length ;
153
+ var startiplong = ulong . Parse ( startip , NumberStyles . HexNumber ) ;
154
+ var endipint = ulong . Parse ( endip , NumberStyles . HexNumber ) ;
155
+ _rangesSortedByIp . Add ( new Range ( ) { Name = description , Start = startiplong , End = endipint } ) ;
156
+ }
94
157
}
95
158
catch ( Exception ex )
96
159
{
97
- Console . WriteLine ( "Unable to read dumped pmip file: " + ex . Message ) ;
160
+ _debugPane . OutputString ( "Unable to read dumped pmip file: " + ex . Message + " \n " ) ;
98
161
}
99
162
163
+ _rangesSortedByIp . Sort ( ( r1 , r2 ) => r1 . Start . CompareTo ( r2 . Start ) ) ;
100
164
}
101
165
102
- public static bool TryGetDescriptionForIp ( ulong ip , out string name )
166
+ private static bool TryGetDescriptionForIp ( ulong ip , out string name )
103
167
{
104
168
name = string . Empty ;
105
169
106
- if ( _rangesSortedByIp == null )
107
- return false ;
108
-
109
170
var rangeToFindIp = new Range ( ) { Start = ip } ;
110
- var index = Array . BinarySearch ( _rangesSortedByIp , rangeToFindIp , _comparer ) ;
171
+ var index = _rangesSortedByIp . BinarySearch ( rangeToFindIp , _comparer ) ;
111
172
112
173
if ( index < 0 )
113
174
return false ;
114
175
115
176
name = _rangesSortedByIp [ index ] . Name ;
116
-
117
177
return true ;
118
178
}
119
179
}
0 commit comments