11using System ;
22using System . Collections . Generic ;
33using System . IO ;
4+ using System . Net . Sockets ;
45using System . Text . RegularExpressions ;
56using Mono . Options ;
67using Mono . Profiler . Aot ;
@@ -11,6 +12,7 @@ namespace aotprofiletool {
1112 class MainClass {
1213 static readonly string Name = "aotprofile-tool" ;
1314
15+ static bool AdbForward ;
1416 static bool Methods ;
1517 static bool Modules ;
1618 static bool Summary ;
@@ -23,6 +25,8 @@ class MainClass {
2325
2426 static string Output ;
2527
28+ static int Port = - 1 ;
29+
2630 static string ProcessArguments ( string [ ] args )
2731 {
2832 var help = false ;
@@ -43,21 +47,27 @@ static string ProcessArguments (string [] args)
4347 { "d|modules" ,
4448 "Show modules in the profile" ,
4549 v => Modules = true } ,
50+ { "f|adb-forward" ,
51+ "Set adb socket forwarding for Android" ,
52+ v => AdbForward = true } ,
4653 { "filter-method=" ,
47- "Filter by method with regex VALUE" ,
54+ "Filter by method with regex { VALUE} " ,
4855 v => FilterMethod = new Regex ( v ) } ,
4956 { "filter-module=" ,
50- "Filter by module with regex VALUE" ,
57+ "Filter by module with regex { VALUE} " ,
5158 v => FilterModule = new Regex ( v ) } ,
5259 { "filter-type=" ,
53- "Filter by type with regex VALUE" ,
60+ "Filter by type with regex { VALUE} " ,
5461 v => FilterType = new Regex ( v ) } ,
5562 { "m|methods" ,
5663 "Show methods in the profile" ,
5764 v => Methods = true } ,
5865 { "o|output=" ,
59- "Write profile to OUTPUT file" ,
66+ "Write profile to { OUTPUT} file" ,
6067 v => Output = v } ,
68+ { "p|port=" ,
69+ "Read profile from aot profiler using local connection on {PORT}" ,
70+ v => int . TryParse ( v , out Port ) } ,
6171 { "s|summary" ,
6272 "Show summary of the profile" ,
6373 v => Summary = true } ,
@@ -79,35 +89,92 @@ static string ProcessArguments (string [] args)
7989 Environment . Exit ( 0 ) ;
8090 }
8191
82- if ( remaining . Count != 1 ) {
83- Error ( "Please specify one <aotprofile-file> to process." ) ;
92+ if ( remaining . Count != 1 && Port < 0 ) {
93+ Error ( "Please specify one <aotprofile-file> to process or network PORT with -p ." ) ;
8494 Environment . Exit ( 2 ) ;
8595 }
8696
87- return remaining [ 0 ] ;
97+ return remaining . Count > 0 ? remaining [ 0 ] : null ;
8898 }
8999
90- public static void Main ( string [ ] args )
100+ static ProfileData ReadProfileFromPort ( ProfileReader reader )
91101 {
92- var path = ProcessArguments ( args ) ;
102+ ProfileData pd ;
93103
94- if ( ! File . Exists ( path ) ) {
95- Error ( $ "'{ path } ' doesn't exist.") ;
96- Environment . Exit ( 3 ) ;
104+ if ( AdbForward ) {
105+ var cmdArgs = $ "forward tcp:{ Port } tcp:{ Port } ";
106+ if ( Verbose )
107+ ColorWriteLine ( $ "Calling 'adb { cmdArgs } '...", ConsoleColor . Yellow ) ;
108+
109+ System . Diagnostics . Process . Start ( "adb" , cmdArgs ) ;
110+ }
111+
112+ using ( var client = new TcpClient ( "127.0.0.1" , Port ) ) {
113+ using ( var stream = client . GetStream ( ) ) {
114+ var msgData = System . Text . Encoding . ASCII . GetBytes ( "save\n " ) ;
115+
116+ stream . Write ( msgData , 0 , msgData . Length ) ;
117+
118+ if ( Verbose )
119+ ColorWriteLine ( $ "Reading from '127.0.0.1:{ Port } '...", ConsoleColor . Yellow ) ;
120+
121+ using ( var memoryStream = new MemoryStream ( 128 * 1024 ) ) {
122+ var data = new byte [ 4 * 1024 ] ;
123+ int len ;
124+
125+ while ( ( len = stream . Read ( data , 0 , data . Length ) ) > 0 ) {
126+ memoryStream . Write ( data , 0 , len ) ;
127+
128+ if ( Verbose )
129+ ColorWrite ( $ "Read { len } bytes...\r ", ConsoleColor . Yellow ) ;
130+ }
131+
132+ if ( Verbose )
133+ ColorWriteLine ( $ "Read total { memoryStream . Length } bytes...", ConsoleColor . Yellow ) ;
134+
135+ memoryStream . Seek ( 0 , SeekOrigin . Begin ) ;
136+
137+ pd = reader . ReadAllData ( memoryStream ) ;
138+ }
139+ }
97140 }
98141
142+ return pd ;
143+ }
144+
145+ public static void Main ( string [ ] args )
146+ {
147+ var path = ProcessArguments ( args ) ;
148+
99149 if ( args . Length == 1 ) {
100150 Modules = Types = Methods = true ;
101151 }
102152
103153 var reader = new ProfileReader ( ) ;
104- ProfileData pd ;
105-
106- using ( var stream = new FileStream ( path , FileMode . Open ) ) {
107- if ( Verbose )
108- ColorWriteLine ( $ "Reading '{ path } '...", ConsoleColor . Yellow ) ;
154+ ProfileData pd = null ;
155+
156+ if ( path == null ) {
157+ if ( Port < 0 ) {
158+ Error ( $ "You should specify path or -p PORT to read the profile.") ;
159+ Environment . Exit ( 4 ) ;
160+ } else {
161+ try {
162+ pd = ReadProfileFromPort ( reader ) ;
163+ } catch ( Exception e ) {
164+ Error ( $ "Unable to read profile through local port: { Port } .\n { e } ") ;
165+ Environment . Exit ( 5 ) ;
166+ }
167+ }
168+ } else if ( ! File . Exists ( path ) ) {
169+ Error ( $ "'{ path } ' doesn't exist.") ;
170+ Environment . Exit ( 3 ) ;
171+ } else {
172+ using ( var stream = new FileStream ( path , FileMode . Open ) ) {
173+ if ( Verbose )
174+ ColorWriteLine ( $ "Reading '{ path } '...", ConsoleColor . Yellow ) ;
109175
110- pd = reader . ReadAllData ( stream ) ;
176+ pd = reader . ReadAllData ( stream ) ;
177+ }
111178 }
112179
113180 List < MethodRecord > methods = new List < MethodRecord > ( pd . Methods ) ;
0 commit comments