44
55using System ;
66using System . Diagnostics ;
7+ using System . IO ;
8+ using System . Text ;
79using Microsoft . DotNet . XHarness . Common . Logging ;
810using Microsoft . DotNet . XHarness . iOS . Shared . Execution ;
911
@@ -17,85 +19,123 @@ public interface IDeviceLogCapturer : IDisposable
1719
1820public class DeviceLogCapturer : IDeviceLogCapturer
1921{
20- private readonly IMlaunchProcessManager _processManager ;
2122 private readonly ILog _mainLog ;
2223 private readonly ILog _deviceLog ;
23- private readonly string _deviceName ;
24+ private readonly string _deviceUdid ;
25+ private readonly string _outputPath ;
26+ private DateTime _startTime ;
2427
25- public DeviceLogCapturer ( IMlaunchProcessManager processManager , ILog mainLog , ILog deviceLog , string deviceName )
28+ public DeviceLogCapturer ( ILog mainLog , ILog deviceLog , string deviceUdid )
2629 {
27- _processManager = processManager ?? throw new ArgumentNullException ( nameof ( processManager ) ) ;
2830 _mainLog = mainLog ?? throw new ArgumentNullException ( nameof ( mainLog ) ) ;
2931 _deviceLog = deviceLog ?? throw new ArgumentNullException ( nameof ( deviceLog ) ) ;
30- _deviceName = deviceName ?? throw new ArgumentNullException ( nameof ( deviceName ) ) ;
31- }
32+ _deviceUdid = deviceUdid ?? throw new ArgumentNullException ( nameof ( deviceUdid ) ) ;
3233
33- private Process _process ;
34+ _outputPath = Path . Combine ( Path . GetTempPath ( ) , $ "device_logs_{ Guid . NewGuid ( ) } .logarchive") ;
35+ }
3436
3537 public void StartCapture ( )
3638 {
37- var args = new MlaunchArguments
38- {
39- new SdkRootArgument ( _processManager . XcodeRoot ) ,
40- new LogDevArgument ( ) ,
41- new DeviceNameArgument ( _deviceName ) ,
42- } ;
43-
44- _process = new Process ( ) ;
45- _process . StartInfo . FileName = _processManager . MlaunchPath ;
46- _process . StartInfo . Arguments = args . AsCommandLine ( ) ;
47- _process . StartInfo . UseShellExecute = false ;
48- _process . StartInfo . RedirectStandardOutput = true ;
49- _process . StartInfo . RedirectStandardError = true ;
50- _process . StartInfo . RedirectStandardInput = true ;
51- _process . OutputDataReceived += ( object sender , DataReceivedEventArgs e ) =>
39+ _startTime = DateTime . Now ;
40+ _deviceLog . WriteLine ( $ "Device log capture started at { _startTime : yyyy-MM-dd HH:mm:ss} ") ;
41+ }
42+
43+ public void StopCapture ( )
44+ {
45+ _deviceLog . WriteLine ( $ "Device log capture stopped at { DateTime . Now : yyyy-MM-dd HH:mm:ss} ") ;
46+
47+ string startTimeStr = _startTime . ToString ( "yyyy-MM-dd HH:mm:ss" ) ;
48+
49+ // Collect logs
50+ string collectArguments = $ "log collect --device-udid { _deviceUdid } --start \" { startTimeStr } \" --output \" { _outputPath } \" ";
51+ _deviceLog . WriteLine ( $ "Collecting logs: sudo { collectArguments } ") ;
52+
53+ using Process collectProcess = new Process ( ) ;
54+ collectProcess . StartInfo . FileName = "sudo" ;
55+ collectProcess . StartInfo . Arguments = collectArguments ;
56+ collectProcess . StartInfo . UseShellExecute = false ;
57+ collectProcess . StartInfo . RedirectStandardOutput = true ;
58+ collectProcess . StartInfo . RedirectStandardError = true ;
59+
60+ StringBuilder collectOutput = new StringBuilder ( ) ;
61+ StringBuilder collectErrors = new StringBuilder ( ) ;
62+
63+ collectProcess . OutputDataReceived += ( sender , e ) =>
5264 {
5365 if ( e . Data != null )
54- {
55- return ;
56- }
66+ collectOutput . AppendLine ( e . Data ) ;
67+ } ;
5768
58- lock ( _deviceLog )
59- {
60- _deviceLog . WriteLine ( e . Data ) ;
61- }
69+ collectProcess . ErrorDataReceived += ( sender , e ) =>
70+ {
71+ if ( e . Data != null )
72+ collectErrors . AppendLine ( e . Data ) ;
6273 } ;
6374
64- _process . ErrorDataReceived += ( object sender , DataReceivedEventArgs e ) =>
75+ collectProcess . Start ( ) ;
76+ collectProcess . BeginOutputReadLine ( ) ;
77+ collectProcess . BeginErrorReadLine ( ) ;
78+ collectProcess . WaitForExit ( ) ;
79+
80+ if ( collectErrors . Length > 0 )
6581 {
66- if ( e . Data == null )
82+ _mainLog . WriteLine ( $ "Errors during log collection: { collectErrors } ") ;
83+
84+ if ( collectProcess . ExitCode != 0 )
6785 {
86+ _deviceLog . WriteLine ( $ "Log collection failed with exit code { collectProcess . ExitCode } . Skipping log reading.") ;
6887 return ;
6988 }
89+ }
7090
71- lock ( _deviceLog )
72- {
73- _deviceLog . WriteLine ( e . Data ) ;
74- }
91+ // Read the collected logs
92+ string readArguments = $ "show \" { _outputPath } \" ";
93+ _deviceLog . WriteLine ( $ "Reading logs: log { readArguments } ") ;
94+
95+ using Process readProcess = new Process ( ) ;
96+ readProcess . StartInfo . FileName = "log" ;
97+ readProcess . StartInfo . Arguments = readArguments ;
98+ readProcess . StartInfo . UseShellExecute = false ;
99+ readProcess . StartInfo . RedirectStandardOutput = true ;
100+ readProcess . StartInfo . RedirectStandardError = true ;
101+
102+ StringBuilder output = new StringBuilder ( ) ;
103+ StringBuilder errors = new StringBuilder ( ) ;
104+
105+ readProcess . OutputDataReceived += ( sender , e ) =>
106+ {
107+ if ( e . Data != null )
108+ output . AppendLine ( e . Data ) ;
75109 } ;
76110
77- _deviceLog . WriteLine ( "{0} {1}" , _process . StartInfo . FileName , _process . StartInfo . Arguments ) ;
111+ readProcess . ErrorDataReceived += ( sender , e ) =>
112+ {
113+ if ( e . Data != null )
114+ errors . AppendLine ( e . Data ) ;
115+ } ;
78116
79- _process . Start ( ) ;
80- _process . BeginOutputReadLine ( ) ;
81- _process . BeginErrorReadLine ( ) ;
82- }
117+ readProcess . Start ( ) ;
118+ readProcess . BeginOutputReadLine ( ) ;
119+ readProcess . BeginErrorReadLine ( ) ;
120+ readProcess . WaitForExit ( ) ;
83121
84- public void StopCapture ( )
85- {
86- if ( _process . HasExited )
122+ if ( output . Length > 0 )
87123 {
88- return ;
124+ lock ( _deviceLog )
125+ {
126+ _deviceLog . WriteLine ( output . ToString ( ) ) ;
127+ }
89128 }
90129
91- _process . StandardInput . WriteLine ( ) ;
92- if ( _process . WaitForExit ( ( int ) TimeSpan . FromSeconds ( 5 ) . TotalMilliseconds ) )
130+ if ( errors . Length > 0 )
93131 {
94- return ;
132+ _mainLog . WriteLine ( $ "Errors while reading device logs: { errors } " ) ;
95133 }
96134
97- _processManager . KillTreeAsync ( _process , _mainLog , diagnostics : false ) . Wait ( ) ;
98- _process . Dispose ( ) ;
135+ if ( Directory . Exists ( _outputPath ) )
136+ {
137+ Directory . Delete ( _outputPath , true ) ;
138+ }
99139 }
100140
101141 public void Dispose ( ) => StopCapture ( ) ;
0 commit comments