11using System ;
22using System . Collections . Generic ;
3+ using System . Diagnostics ;
34using System . Globalization ;
45using System . IO ;
6+ using Microsoft . VisualStudio ;
7+ using Microsoft . VisualStudio . Debugger ;
58using Microsoft . VisualStudio . Debugger . CallStack ;
69using Microsoft . VisualStudio . Debugger . ComponentInterfaces ;
10+ using Microsoft . VisualStudio . Shell ;
11+ using Microsoft . VisualStudio . Shell . Interop ;
712
813namespace PmipMyCallStack
914{
10- public class PmipCallStackFilter : IDkmCallStackFilter
15+ public class PmipCallStackFilter : IDkmCallStackFilter , IDkmLoadCompleteNotification
1116 {
12- private static Range [ ] _rangesSortedByIp ;
13- private static long _previousFileLength ;
17+ private static List < Range > _rangesSortedByIp = new List < Range > ( ) ;
1418 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+ }
1539
1640 public DkmStackWalkFrame [ ] FilterNextFrame ( DkmStackContext stackContext , DkmStackWalkFrame input )
1741 {
@@ -27,14 +51,15 @@ public DkmStackWalkFrame[] FilterNextFrame(DkmStackContext stackContext, DkmStac
2751 if ( ! stackContext . Thread . IsMainThread ) // error case
2852 return new [ ] { input } ;
2953
54+ if ( ! _enabled ) // environment variable not set
55+ return new [ ] { input } ;
3056
3157 return new [ ] { PmipStackFrame ( stackContext , input ) } ;
3258 }
3359
34- public static DkmStackWalkFrame PmipStackFrame ( DkmStackContext stackContext , DkmStackWalkFrame frame )
60+ private static DkmStackWalkFrame PmipStackFrame ( DkmStackContext stackContext , DkmStackWalkFrame frame )
3561 {
36- var fileName = Path . Combine ( Path . GetTempPath ( ) , "pmip." + frame . Process . LivePart . Id ) ;
37- RefreshStackData ( fileName ) ;
62+ RefreshStackData ( frame . Process . LivePart . Id ) ;
3863 string name = null ;
3964 if ( TryGetDescriptionForIp ( frame . InstructionAddress . CPUInstructionPart . InstructionPointer , out name ) )
4065 return DkmStackWalkFrame . Create (
@@ -50,70 +75,105 @@ public static DkmStackWalkFrame PmipStackFrame(DkmStackContext stackContext, Dkm
5075 return frame ;
5176 }
5277
53- public static void RefreshStackData ( string fileName )
78+ private static int GetFileNameSequenceNum ( string path )
5479 {
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 )
56109 {
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 ) ;
59119
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 ] ) ;
63124
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 )
66129 {
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 ;
89134 }
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 ] ;
90152
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+ }
94157 }
95158 catch ( Exception ex )
96159 {
97- Console . WriteLine ( "Unable to read dumped pmip file: " + ex . Message ) ;
160+ _debugPane . OutputString ( "Unable to read dumped pmip file: " + ex . Message + " \n " ) ;
98161 }
99162
163+ _rangesSortedByIp . Sort ( ( r1 , r2 ) => r1 . Start . CompareTo ( r2 . Start ) ) ;
100164 }
101165
102- public static bool TryGetDescriptionForIp ( ulong ip , out string name )
166+ private static bool TryGetDescriptionForIp ( ulong ip , out string name )
103167 {
104168 name = string . Empty ;
105169
106- if ( _rangesSortedByIp == null )
107- return false ;
108-
109170 var rangeToFindIp = new Range ( ) { Start = ip } ;
110- var index = Array . BinarySearch ( _rangesSortedByIp , rangeToFindIp , _comparer ) ;
171+ var index = _rangesSortedByIp . BinarySearch ( rangeToFindIp , _comparer ) ;
111172
112173 if ( index < 0 )
113174 return false ;
114175
115176 name = _rangesSortedByIp [ index ] . Name ;
116-
117177 return true ;
118178 }
119179 }
0 commit comments