@@ -341,6 +341,7 @@ type symbolInfo struct {
341
341
}
342
342
343
343
type pcRange struct {
344
+ // [start; end)
344
345
start uint64
345
346
end uint64
346
347
unit * CompileUnit
@@ -351,7 +352,32 @@ type pcFixFn = (func([2]uint64) ([2]uint64, bool))
351
352
func readTextRanges (debugInfo * dwarf.Data , module * vminfo.KernelModule , pcFix pcFixFn ) (
352
353
[]pcRange , []* CompileUnit , error ) {
353
354
var ranges []pcRange
354
- var units []* CompileUnit
355
+ unitMap := map [string ]* CompileUnit {}
356
+ addRange := func (r [2 ]uint64 , fileName string ) {
357
+ if pcFix != nil {
358
+ var filtered bool
359
+ r , filtered = pcFix (r )
360
+ if filtered {
361
+ return
362
+ }
363
+ }
364
+ unit , ok := unitMap [fileName ]
365
+ if ! ok {
366
+ unit = & CompileUnit {
367
+ ObjectUnit : ObjectUnit {
368
+ Name : fileName ,
369
+ },
370
+ Module : module ,
371
+ }
372
+ unitMap [fileName ] = unit
373
+ }
374
+ if module .Name == "" {
375
+ ranges = append (ranges , pcRange {r [0 ], r [1 ], unit })
376
+ } else {
377
+ ranges = append (ranges , pcRange {r [0 ] + module .Addr , r [1 ] + module .Addr , unit })
378
+ }
379
+ }
380
+
355
381
for r := debugInfo .Reader (); ; {
356
382
ent , err := r .Next ()
357
383
if err != nil {
@@ -363,41 +389,97 @@ func readTextRanges(debugInfo *dwarf.Data, module *vminfo.KernelModule, pcFix pc
363
389
if ent .Tag != dwarf .TagCompileUnit {
364
390
return nil , nil , fmt .Errorf ("found unexpected tag %v on top level" , ent .Tag )
365
391
}
366
- attrName := ent .Val (dwarf .AttrName )
367
- if attrName == nil {
392
+ attrName , ok := ent .Val (dwarf .AttrName ).( string )
393
+ if ! ok {
368
394
continue
369
395
}
370
- unit := & CompileUnit {
371
- ObjectUnit : ObjectUnit {
372
- Name : attrName .(string ),
373
- },
374
- Module : module ,
375
- }
376
- units = append (units , unit )
377
- ranges1 , err := debugInfo .Ranges (ent )
378
- if err != nil {
379
- return nil , nil , err
380
- }
396
+ attrCompDir , _ := ent .Val (dwarf .AttrCompDir ).(string )
381
397
382
- var filtered bool
383
- for _ , r := range ranges1 {
384
- if pcFix != nil {
385
- r , filtered = pcFix (r )
386
- if filtered {
387
- continue
388
- }
398
+ const languageRust = 28
399
+ if language , ok := ent .Val (dwarf .AttrLanguage ).(int64 ); ok && language == languageRust {
400
+ rawRanges , err := rustRanges (debugInfo , ent )
401
+ if err != nil {
402
+ return nil , nil , fmt .Errorf ("failed to query Rust PC ranges: %w" , err )
389
403
}
390
- if module .Name == "" {
391
- ranges = append (ranges , pcRange {r [0 ], r [1 ], unit })
392
- } else {
393
- ranges = append (ranges , pcRange {r [0 ] + module .Addr , r [1 ] + module .Addr , unit })
404
+ for _ , r := range rawRanges {
405
+ addRange ([2 ]uint64 {r .start , r .end }, r .file )
406
+ }
407
+ } else {
408
+ // Compile unit names are relative to the compilation dir,
409
+ // while per-line info isn't.
410
+ // Let's stick to the common approach.
411
+ unitName := filepath .Join (attrCompDir , attrName )
412
+ ranges1 , err := debugInfo .Ranges (ent )
413
+ if err != nil {
414
+ return nil , nil , err
415
+ }
416
+ for _ , r := range ranges1 {
417
+ addRange (r , unitName )
394
418
}
395
419
}
396
420
r .SkipChildren ()
397
421
}
422
+ var units []* CompileUnit
423
+ for _ , unit := range unitMap {
424
+ units = append (units , unit )
425
+ }
398
426
return ranges , units , nil
399
427
}
400
428
429
+ type rustRange struct {
430
+ // [start; end)
431
+ start uint64
432
+ end uint64
433
+ file string
434
+ }
435
+
436
+ func rustRanges (debugInfo * dwarf.Data , ent * dwarf.Entry ) ([]rustRange , error ) {
437
+ // For Rust, a single compilation unit may comprise all .rs files that belong to the crate.
438
+ // To properly render the coverage, we need to somehow infer the ranges that belong to
439
+ // those individual .rs files.
440
+ // For simplicity, let's create fake ranges by looking at the DWARF line information.
441
+ var ret []rustRange
442
+ lr , err := debugInfo .LineReader (ent )
443
+ if err != nil {
444
+ return nil , fmt .Errorf ("failed to query line reader: %w" , err )
445
+ }
446
+ var startPC uint64
447
+ var files []string
448
+ for {
449
+ var entry dwarf.LineEntry
450
+ if err = lr .Next (& entry ); err != nil {
451
+ if err == io .EOF {
452
+ break
453
+ }
454
+ return nil , fmt .Errorf ("failed to parse next line entry: %w" , err )
455
+ }
456
+ if startPC == 0 || entry .Address != startPC {
457
+ for _ , file := range files {
458
+ ret = append (ret , rustRange {
459
+ start : startPC ,
460
+ end : entry .Address ,
461
+ file : file ,
462
+ })
463
+ }
464
+ files = files [:0 ]
465
+ startPC = entry .Address
466
+ }
467
+ // Keep on collecting file names that are covered by the range.
468
+ files = append (files , entry .File .Name )
469
+ }
470
+ if startPC != 0 {
471
+ // We don't know the end PC for these, but let's still add them to the ranges.
472
+ for _ , file := range files {
473
+ ret = append (ret , rustRange {
474
+ start : startPC ,
475
+ end : startPC + 1 ,
476
+ file : file ,
477
+ })
478
+ }
479
+ }
480
+ return ret , nil
481
+ }
482
+
401
483
func symbolizeModule (target * targets.Target , interner * symbolizer.Interner , kernelDirs * mgrconfig.KernelDirs ,
402
484
splitBuildDelimiters []string , mod * vminfo.KernelModule , pcs []uint64 ) ([]* Frame , error ) {
403
485
procs := min (runtime .GOMAXPROCS (0 )/ 2 , len (pcs )/ 1000 )
0 commit comments