1010using System . Diagnostics ;
1111using System . Globalization ;
1212using System . IO ;
13+ using System . Linq ;
1314using System . Text ;
1415using System . Text . RegularExpressions ;
1516using System . Threading ;
@@ -30,6 +31,7 @@ public class AndroidLogcatLogParser : LogParserBase<AndroidLogcatLogParsedEntry,
3031
3132 const long SECONDS_TO_NS = 1000000000 ;
3233 const long MS_TO_NS = 1000000 ;
34+ const long US_TO_NS = 1000 ;
3335 static readonly TimestampDelta OneNanoSecondTimestampDelta = new TimestampDelta ( 1 ) ; // At least 1ns timestamp for duration
3436
3537 public AndroidLogcatLogParser ( string [ ] filePaths ) : base ( filePaths )
@@ -55,7 +57,6 @@ public override void ProcessSource(
5557 string line = string . Empty ;
5658 var logEntries = new List < LogEntry > ( ) ;
5759 var durationLogEntries = new List < DurationLogEntry > ( ) ;
58- LogEntry logEntry = null ;
5960
6061 string timeDateFormat = null ;
6162
@@ -72,6 +73,7 @@ public override void ProcessSource(
7273
7374 while ( ( line = file . ReadLine ( ) ) != null )
7475 {
76+ LogEntry logEntry = null ;
7577 // Optimization - don't save blank lines
7678 if ( line == String . Empty )
7779 {
@@ -223,6 +225,24 @@ public override void ProcessSource(
223225 durationLogEntry = LogEntryFromDurationMs ( durationMs , logEntry , logEntry . Tag ) ;
224226 }
225227 }
228+ // "Creating child chains: 13886us" - UserDebug
229+ else if ( logEntry . Tag == "netd" && logEntry . Message . EndsWith ( "us" ) )
230+ {
231+ var messageSplit = logEntry . Message . Split ( ) ;
232+ if ( int . TryParse ( messageSplit [ ^ 1 ] . Replace ( "us" , String . Empty ) , out int durationUs ) )
233+ {
234+ durationLogEntry = LogEntryFromDurationUs ( durationUs , logEntry , logEntry . Tag ) ;
235+ }
236+ }
237+ // "RenderEngine: shader cache generated - 48 shaders in 1452.951172 ms" - UserDebug
238+ else if ( logEntry . Tag == "RenderEngine" && logEntry . Message . StartsWith ( "shader cache generated" ) && logEntry . Message . EndsWith ( " ms" ) )
239+ {
240+ var messageSplit = logEntry . Message . Split ( ) ;
241+ if ( messageSplit . Length >= 9 && double . TryParse ( messageSplit [ 7 ] , out double durationMs ) )
242+ {
243+ durationLogEntry = LogEntryFromDurationMs ( durationMs , logEntry , logEntry . Tag ) ;
244+ }
245+ }
226246 // Android 12
227247 else if ( logEntry . Tag == "ServiceManager" && logEntry . Message . Contains ( "successful after waiting" ) )
228248 {
@@ -262,7 +282,6 @@ public override void ProcessSource(
262282 if ( durationLogEntry != null )
263283 {
264284 durationLogEntries . Add ( durationLogEntry ) ;
265- dataProcessor . ProcessDataElement ( durationLogEntry , Context , cancellationToken ) ;
266285 }
267286 }
268287 else
@@ -279,24 +298,50 @@ public override void ProcessSource(
279298 if ( logEntry != null )
280299 {
281300 logEntries . Add ( logEntry ) ;
282- dataProcessor . ProcessDataElement ( logEntry , Context , cancellationToken ) ;
283301 }
284302
285303 currentLineNumber ++ ;
286304 }
287305
288- // Now adjust to start of trace
306+ // Synthetic durations
307+
308+ // Kernel Boot
309+ var kernelStart = logEntries . SingleOrDefault ( f => f . LineNumber <= 100 && f . Tag == String . Empty && f . Message . StartsWith ( "Linux version" ) ) ;
310+ var initStart = logEntries . SingleOrDefault ( f => f . Tag == "init" && f . Message == "init first stage started!" ) ;
311+ if ( kernelStart != null && initStart != null )
312+ {
313+ const string kernelBoot = "Kernel Boot" ;
314+ var kernelBootLogEntry = new DurationLogEntry ( )
315+ {
316+ StartTimestamp = kernelStart . Timestamp ,
317+ EndTimestamp = initStart . Timestamp ,
318+ Duration = initStart . Timestamp - kernelStart . Timestamp ,
319+ FilePath = initStart . FilePath ,
320+ LineNumber = initStart . LineNumber ,
321+ PID = initStart . PID ,
322+ TID = initStart . TID ,
323+ Priority = initStart . Priority ,
324+ Message = kernelBoot ,
325+ Name = kernelBoot ,
326+ } ;
327+ durationLogEntries . Add ( kernelBootLogEntry ) ;
328+ }
329+
330+ // Now adjust times to start of earliest message given out of order timestamps
289331 foreach ( var le in logEntries )
290332 {
291333 if ( le . Timestamp != Timestamp . MinValue )
292334 {
293335 le . Timestamp = Timestamp . FromNanoseconds ( le . Timestamp . ToNanoseconds - startNanoSeconds ) ;
294336 }
337+ dataProcessor . ProcessDataElement ( le , Context , cancellationToken ) ;
295338 }
296339 foreach ( var durationLogEntry in durationLogEntries )
297340 {
298341 durationLogEntry . StartTimestamp = Timestamp . FromNanoseconds ( durationLogEntry . StartTimestamp . ToNanoseconds - startNanoSeconds ) ;
299342 durationLogEntry . EndTimestamp = Timestamp . FromNanoseconds ( durationLogEntry . EndTimestamp . ToNanoseconds - startNanoSeconds ) ;
343+
344+ dataProcessor . ProcessDataElement ( durationLogEntry , Context , cancellationToken ) ;
300345 }
301346
302347 contentDictionary [ path ] = logEntries . AsReadOnly ( ) ;
@@ -337,6 +382,11 @@ private DurationLogEntry LogEntryFromDurationMs(double durationMs, LogEntry logE
337382 return LogEntryFromDurationNs ( ( long ) ( durationMs * MS_TO_NS ) , logEntry , name ) ;
338383 }
339384
385+ private DurationLogEntry LogEntryFromDurationUs ( int durationUs , LogEntry logEntry , string name )
386+ {
387+ return LogEntryFromDurationNs ( durationUs * US_TO_NS , logEntry , name ) ;
388+ }
389+
340390 private DurationLogEntry LogEntryFromDurationNs ( long durationNs , LogEntry logEntry , string name )
341391 {
342392 if ( durationNs == 0 )
0 commit comments